Merge "Enabled SYS_MODULE capability for wifi@1.0-service"
diff --git a/OWNERS b/OWNERS
index 682a067..1d319af 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,5 @@
 enh@google.com
+per-file libsysutils/src/Netlink* = ek@google.com
+per-file libsysutils/src/Netlink* = lorenzo@google.com
+per-file libsysutils/include/sysutils/Netlink* = ek@google.com
+per-file libsysutils/include/sysutils/Netlink* = lorenzo@google.com
diff --git a/adb/Android.bp b/adb/Android.bp
index 0858a6c..d81bb4b 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -12,6 +12,336 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+cc_defaults {
+    name: "adb_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-missing-field-initializers",
+        "-Wvla",
+    ],
+    rtti: true,
+
+    clang_cflags: [
+        "-Wexit-time-destructors",
+        "-Wthread-safety",
+    ],
+
+    use_version_lib: true,
+
+    compile_multilib: "first",
+    product_variables: {
+        debuggable: {
+            cflags: [
+                "-DALLOW_ADBD_ROOT",
+                "-DALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_NO_AUTH",
+            ],
+        },
+    },
+
+    target: {
+        android: {
+            cflags: ["-DADB_HOST=0"],
+        },
+
+        host: {
+            cflags: ["-DADB_HOST=1"],
+        },
+
+        darwin: {
+            host_ldlibs: [
+                "-lpthread",
+                "-framework CoreFoundation",
+                "-framework IOKit",
+                "-lobjc",
+            ],
+        },
+
+        windows: {
+            cflags: [
+                // Define windows.h and tchar.h Unicode preprocessor symbols so that
+                // CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
+                // build if you accidentally pass char*. Fix by calling like:
+                //   std::wstring path_wide;
+                //   if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
+                //   CreateFileW(path_wide.c_str());
+                "-DUNICODE=1",
+                "-D_UNICODE=1",
+
+                // -std=gnu++11 doesn't set _GNU_SOURCE on Windows.
+                "-D_GNU_SOURCE",
+
+                // MinGW hides some things behind _POSIX_SOURCE.
+                "-D_POSIX_SOURCE",
+            ],
+
+            host_ldlibs: [
+                "-lws2_32",
+                "-lgdi32",
+                "-luserenv",
+            ],
+        },
+    },
+}
+
+// libadb
+// =========================================================
+// These files are compiled for both the host and the device.
+libadb_srcs = [
+    "adb.cpp",
+    "adb_io.cpp",
+    "adb_listeners.cpp",
+    "adb_trace.cpp",
+    "adb_utils.cpp",
+    "fdevent.cpp",
+    "services.cpp",
+    "sockets.cpp",
+    "socket_spec.cpp",
+    "sysdeps/errno.cpp",
+    "transport.cpp",
+    "transport_local.cpp",
+    "transport_usb.cpp",
+]
+
+libadb_posix_srcs = [
+    "sysdeps_unix.cpp",
+    "sysdeps/posix/network.cpp",
+]
+
+libadb_test_srcs = [
+    "adb_io_test.cpp",
+    "adb_listeners_test.cpp",
+    "adb_utils_test.cpp",
+    "fdevent_test.cpp",
+    "socket_spec_test.cpp",
+    "socket_test.cpp",
+    "sysdeps_test.cpp",
+    "sysdeps/stat_test.cpp",
+    "transport_test.cpp",
+]
+
+cc_library_host_static {
+    name: "libadb_host",
+    defaults: ["adb_defaults"],
+
+    srcs: libadb_srcs + [
+        "client/auth.cpp",
+        "client/usb_libusb.cpp",
+        "client/usb_dispatch.cpp",
+        "client/transport_mdns.cpp",
+    ],
+
+    target: {
+        linux: {
+            srcs: ["client/usb_linux.cpp"],
+        },
+        darwin: {
+            srcs: ["client/usb_osx.cpp"],
+        },
+
+        not_windows: {
+            srcs: libadb_posix_srcs,
+        },
+        windows: {
+            enabled: true,
+            srcs: [
+                "client/usb_windows.cpp",
+                "sysdeps_win32.cpp",
+                "sysdeps/win32/errno.cpp",
+                "sysdeps/win32/stat.cpp",
+            ],
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+
+    static_libs: [
+        "libbase",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "libmdnssd",
+        "libusb",
+    ],
+}
+
+cc_test_host {
+    name: "adb_test",
+    defaults: ["adb_defaults"],
+    srcs: libadb_test_srcs,
+    static_libs: [
+        "libadb_host",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libmdnssd",
+        "libdiagnose_usb",
+        "libusb",
+    ],
+
+    target: {
+        windows: {
+            enabled: true,
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+}
+
+cc_binary_host {
+    name: "adb",
+    tags: ["debug"],
+
+    defaults: ["adb_defaults"],
+
+    srcs: [
+        "client/adb_client.cpp",
+        "client/bugreport.cpp",
+        "client/commandline.cpp",
+        "client/file_sync_client.cpp",
+        "client/main.cpp",
+        "client/console.cpp",
+        "client/line_printer.cpp",
+        "shell_service_protocol.cpp",
+    ],
+
+    static_libs: [
+        "libadb_host",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "liblog",
+        "libmdnssd",
+        "libusb",
+    ],
+
+    stl: "libc++_static",
+
+    // Don't add anything here, we don't want additional shared dependencies
+    // on the host adb tool, and shared libraries that link against libc++
+    // will violate ODR
+    shared_libs: [],
+
+    target: {
+        darwin: {
+            cflags: [
+                "-Wno-sizeof-pointer-memaccess",
+            ],
+        },
+        windows: {
+            enabled: true,
+            ldflags: ["-municode"],
+            shared_libs: ["AdbWinApi"],
+            required: [
+                "AdbWinUsbApi",
+            ],
+        },
+    },
+}
+
+cc_library_static {
+    name: "libadbd",
+    defaults: ["adb_defaults"],
+
+    // libminadbd wants both, for some reason.
+    compile_multilib: "both",
+    srcs: libadb_srcs + libadb_posix_srcs + [
+        "daemon/auth.cpp",
+        "daemon/usb.cpp",
+        "daemon/jdwp_service.cpp",
+    ],
+
+    static_libs: [
+        "libasyncio",
+        "libbootloader_message",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "libqemu_pipe",
+        "libbase",
+    ],
+}
+
+cc_binary {
+    name: "adbd",
+    defaults: ["adb_defaults"],
+
+    // adbd must be static, as it is copied into the recovery image.
+    static_executable: true,
+
+    srcs: [
+        "daemon/main.cpp",
+        "daemon/mdns.cpp",
+        "daemon/file_sync_service.cpp",
+        "daemon/framebuffer_service.cpp",
+        "daemon/remount_service.cpp",
+        "daemon/set_verity_enable_state_service.cpp",
+        "daemon/shell_service.cpp",
+        "shell_service_protocol.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    strip: {
+        keep_symbols: true,
+    },
+
+    static_libs: [
+        "libadbd",
+        "libasyncio",
+        "libavb_user",
+        "libbootloader_message",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "libfec",
+        "libfec_rs",
+        "libfs_mgr",
+        "liblog",
+        "libext4_utils",
+        "libmdnssd",
+        "libminijail",
+        "libselinux",
+        "libsquashfs_utils",
+        "libqemu_pipe",
+        "libdebuggerd_handler",
+
+        "libbase",
+        "libcutils",
+    ],
+}
+
+cc_test {
+    name: "adbd_test",
+    defaults: ["adb_defaults"],
+    srcs: libadb_test_srcs + [
+        "daemon/shell_service.cpp",
+        "daemon/shell_service_test.cpp",
+        "shell_service_protocol.cpp",
+        "shell_service_protocol_test.cpp",
+    ],
+
+    static_libs: [
+        "libadbd",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "liblog",
+        "libusb",
+        "libmdnssd",
+    ],
+}
+
 python_binary_host {
     name: "adb_integration_test_adb",
     main: "test_adb.py",
diff --git a/adb/Android.mk b/adb/Android.mk
index e52f0cb..8b2d558 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -1,387 +1,8 @@
-# Copyright 2005 The Android Open Source Project
-#
-# Android.mk for adb
-#
+LOCAL_PATH := $(call my-dir)
 
-LOCAL_PATH:= $(call my-dir)
+# Archive adb, adb.exe.
+$(call dist-for-goals,dist_files sdk win_sdk,$(HOST_OUT_EXECUTABLES)/adb)
 
-include $(LOCAL_PATH)/../platform_tools_tool_version.mk
-
-adb_host_sanitize :=
-adb_target_sanitize :=
-
-ADB_COMMON_CFLAGS := \
-    -frtti \
-    -Wall -Wextra -Werror \
-    -Wno-unused-parameter \
-    -Wno-missing-field-initializers \
-    -Wvla \
-    -DADB_VERSION="\"$(tool_version)\"" \
-
-ADB_COMMON_posix_CFLAGS := \
-    -Wexit-time-destructors \
-    -Wthread-safety \
-
-ADB_COMMON_linux_CFLAGS := \
-    $(ADB_COMMON_posix_CFLAGS) \
-
-ADB_COMMON_darwin_CFLAGS := \
-    $(ADB_COMMON_posix_CFLAGS) \
-
-# Define windows.h and tchar.h Unicode preprocessor symbols so that
-# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
-# build if you accidentally pass char*. Fix by calling like:
-#   std::wstring path_wide;
-#   if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
-#   CreateFileW(path_wide.c_str());
-ADB_COMMON_windows_CFLAGS := \
-    -DUNICODE=1 -D_UNICODE=1 \
-
-# libadb
-# =========================================================
-
-# Much of adb is duplicated in bootable/recovery/minadb and fastboot. Changes
-# made to adb rarely get ported to the other two, so the trees have diverged a
-# bit. We'd like to stop this because it is a maintenance nightmare, but the
-# divergence makes this difficult to do all at once. For now, we will start
-# small by moving common files into a static library. Hopefully some day we can
-# get enough of adb in here that we no longer need minadb. https://b/17626262
-LIBADB_SRC_FILES := \
-    adb.cpp \
-    adb_io.cpp \
-    adb_listeners.cpp \
-    adb_trace.cpp \
-    adb_utils.cpp \
-    fdevent.cpp \
-    sockets.cpp \
-    socket_spec.cpp \
-    sysdeps/errno.cpp \
-    transport.cpp \
-    transport_local.cpp \
-    transport_usb.cpp \
-
-LIBADB_TEST_SRCS := \
-    adb_io_test.cpp \
-    adb_listeners_test.cpp \
-    adb_utils_test.cpp \
-    fdevent_test.cpp \
-    socket_spec_test.cpp \
-    socket_test.cpp \
-    sysdeps_test.cpp \
-    sysdeps/stat_test.cpp \
-    transport_test.cpp \
-
-LIBADB_CFLAGS := \
-    $(ADB_COMMON_CFLAGS) \
-    -fvisibility=hidden \
-
-LIBADB_linux_CFLAGS := \
-    $(ADB_COMMON_linux_CFLAGS) \
-
-LIBADB_darwin_CFLAGS := \
-    $(ADB_COMMON_darwin_CFLAGS) \
-
-LIBADB_windows_CFLAGS := \
-    $(ADB_COMMON_windows_CFLAGS) \
-
-LIBADB_darwin_SRC_FILES := \
-    sysdeps_unix.cpp \
-    sysdeps/posix/network.cpp \
-    client/usb_dispatch.cpp \
-    client/usb_libusb.cpp \
-    client/usb_osx.cpp \
-
-LIBADB_linux_SRC_FILES := \
-    sysdeps_unix.cpp \
-    sysdeps/posix/network.cpp \
-    client/usb_dispatch.cpp \
-    client/usb_libusb.cpp \
-    client/usb_linux.cpp \
-
-LIBADB_windows_SRC_FILES := \
-    sysdeps_win32.cpp \
-    sysdeps/win32/errno.cpp \
-    sysdeps/win32/stat.cpp \
-    client/usb_dispatch.cpp \
-    client/usb_libusb.cpp \
-    client/usb_windows.cpp \
-
-LIBADB_TEST_windows_SRCS := \
-    sysdeps/win32/errno_test.cpp \
-    sysdeps_win32_test.cpp \
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadbd_usb
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
-LOCAL_SRC_FILES := daemon/usb.cpp
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libasyncio
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadbd
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
-LOCAL_SRC_FILES := \
-    $(LIBADB_SRC_FILES) \
-    adbd_auth.cpp \
-    jdwp_service.cpp \
-    sysdeps/posix/network.cpp \
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadb
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
-LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
-LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
-LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
-LOCAL_SRC_FILES := \
-    $(LIBADB_SRC_FILES) \
-    adb_auth_host.cpp \
-    transport_mdns.cpp \
-
-LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES)
-LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES)
-LOCAL_SRC_FILES_windows := $(LIBADB_windows_SRC_FILES)
-
-LOCAL_SANITIZE := $(adb_host_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libmdnssd libusb
-
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
-LOCAL_MULTILIB := first
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := adbd_test
-LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := \
-    $(LIBADB_TEST_SRCS) \
-    $(LIBADB_TEST_linux_SRCS) \
-    shell_service.cpp \
-    shell_service_protocol.cpp \
-    shell_service_protocol_test.cpp \
-    shell_service_test.cpp \
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto libusb libmdnssd
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
-include $(BUILD_NATIVE_TEST)
-
-# libdiagnose_usb
-# =========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libdiagnose_usb
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := diagnose_usb.cpp
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libbase
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-# adb_test
-# =========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := adb_test
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
-LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
-LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
-LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
-LOCAL_SRC_FILES := \
-    $(LIBADB_TEST_SRCS) \
-    adb_client.cpp \
-    bugreport.cpp \
-    bugreport_test.cpp \
-    line_printer.cpp \
-    services.cpp \
-    shell_service_protocol.cpp \
-    shell_service_protocol_test.cpp \
-
-LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
-LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
-LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
-LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_STATIC_LIBRARIES := \
-    libadb \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libcutils \
-    libdiagnose_usb \
-    libmdnssd \
-    libgmock_host \
-    libusb \
-
-# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
-LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit -lobjc
-LOCAL_LDLIBS_windows := -lws2_32 -luserenv
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-
-LOCAL_MULTILIB := first
-
-include $(BUILD_HOST_NATIVE_TEST)
-
-# adb host tool
-# =========================================================
-include $(CLEAR_VARS)
-
-LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-
-LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon -lobjc
-
-# Use wmain instead of main
-LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinUsbApi
-
-LOCAL_SRC_FILES := \
-    adb_client.cpp \
-    bugreport.cpp \
-    client/main.cpp \
-    console.cpp \
-    commandline.cpp \
-    file_sync_client.cpp \
-    line_printer.cpp \
-    services.cpp \
-    shell_service_protocol.cpp \
-
-LOCAL_CFLAGS += \
-    $(ADB_COMMON_CFLAGS) \
-    -D_GNU_SOURCE \
-    -DADB_HOST=1 \
-
-LOCAL_CFLAGS_windows := \
-    $(ADB_COMMON_windows_CFLAGS)
-
-LOCAL_CFLAGS_linux := \
-    $(ADB_COMMON_linux_CFLAGS) \
-
-LOCAL_CFLAGS_darwin := \
-    $(ADB_COMMON_darwin_CFLAGS) \
-    -Wno-sizeof-pointer-memaccess -Wno-unused-parameter \
-
-LOCAL_MODULE := adb
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_STATIC_LIBRARIES := \
-    libadb \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libdiagnose_usb \
-    liblog \
-    libmdnssd \
-    libusb \
-
-# Don't use libcutils on Windows.
-LOCAL_STATIC_LIBRARIES_darwin := libcutils
-LOCAL_STATIC_LIBRARIES_linux := libcutils
-
-LOCAL_CXX_STL := libc++_static
-
-# Don't add anything here, we don't want additional shared dependencies
-# on the host adb tool, and shared libraries that link against libc++
-# will violate ODR
-LOCAL_SHARED_LIBRARIES :=
-
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files sdk win_sdk,$(LOCAL_BUILT_MODULE))
 ifdef HOST_CROSS_OS
-# Archive adb.exe for win_sdk build.
-$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
+$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
 endif
-
-
-# adbd device daemon
-# =========================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    daemon/main.cpp \
-    daemon/mdns.cpp \
-    services.cpp \
-    file_sync_service.cpp \
-    framebuffer_service.cpp \
-    remount_service.cpp \
-    set_verity_enable_state_service.cpp \
-    shell_service.cpp \
-    shell_service_protocol.cpp \
-
-LOCAL_CFLAGS := \
-    $(ADB_COMMON_CFLAGS) \
-    $(ADB_COMMON_linux_CFLAGS) \
-    -DADB_HOST=0 \
-    -D_GNU_SOURCE \
-    -Wno-deprecated-declarations \
-
-LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
-
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
-LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
-endif
-
-LOCAL_MODULE := adbd
-
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STRIP_MODULE := keep_symbols
-LOCAL_STATIC_LIBRARIES := \
-    libadbd \
-    libasyncio \
-    libavb_user \
-    libbase \
-    libqemu_pipe \
-    libbootloader_message \
-    libfs_mgr \
-    libfec \
-    libfec_rs \
-    libselinux \
-    liblog \
-    libext4_utils \
-    libsquashfs_utils \
-    libcutils \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libminijail \
-    libmdnssd \
-    libdebuggerd_handler \
-
-include $(BUILD_EXECUTABLE)
-
-# adb integration test
-# =========================================================
-$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
-$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/CPPLINT.cfg b/adb/CPPLINT.cfg
deleted file mode 100644
index f496490..0000000
--- a/adb/CPPLINT.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-set noparent
-filter=-build/header_guard,-build/include,-readability/function,-whitespace/indent
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ee3503b..65fa2e7 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -42,9 +42,9 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/parsenetaddress.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <build/version.h>
 
 #include "adb_auth.h"
 #include "adb_io.h"
@@ -67,8 +67,8 @@
         "Android Debug Bridge version %d.%d.%d\n"
         "Version %s\n"
         "Installed as %s\n",
-        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION,
-        android::base::GetExecutablePath().c_str());
+        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+        android::build::GetBuildNumber().c_str(), android::base::GetExecutablePath().c_str());
 }
 
 void fatal(const char *fmt, ...) {
@@ -105,31 +105,27 @@
 }
 
 uint32_t calculate_apacket_checksum(const apacket* p) {
-    const unsigned char* x = reinterpret_cast<const unsigned char*>(p->data);
     uint32_t sum = 0;
-    size_t count = p->msg.data_length;
-
-    while (count-- > 0) {
-        sum += *x++;
+    for (size_t i = 0; i < p->msg.data_length; ++i) {
+        sum += static_cast<uint8_t>(p->payload[i]);
     }
-
     return sum;
 }
 
 apacket* get_apacket(void)
 {
-    apacket* p = reinterpret_cast<apacket*>(malloc(sizeof(apacket)));
+    apacket* p = new apacket();
     if (p == nullptr) {
       fatal("failed to allocate an apacket");
     }
 
-    memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+    memset(&p->msg, 0, sizeof(p->msg));
     return p;
 }
 
 void put_apacket(apacket *p)
 {
-    free(p);
+    delete p;
 }
 
 void handle_online(atransport *t)
@@ -140,8 +136,16 @@
 
 void handle_offline(atransport *t)
 {
-    D("adb: offline");
-    //Close the associated usb
+    if (t->GetConnectionState() == kCsOffline) {
+        LOG(INFO) << t->serial_name() << ": already offline";
+        return;
+    }
+
+    LOG(INFO) << t->serial_name() << ": offline";
+
+    t->SetConnectionState(kCsOffline);
+
+    // Close the associated usb
     t->online = 0;
 
     // This is necessary to avoid a race condition that occurred when a transport closes
@@ -155,8 +159,7 @@
 #define DUMPMAX 32
 void print_packet(const char *label, apacket *p)
 {
-    char *tag;
-    char *x;
+    const char* tag;
     unsigned count;
 
     switch(p->msg.command){
@@ -173,15 +176,15 @@
     fprintf(stderr, "%s: %s %08x %08x %04x \"",
             label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
     count = p->msg.data_length;
-    x = (char*) p->data;
-    if(count > DUMPMAX) {
+    const char* x = p->payload.data();
+    if (count > DUMPMAX) {
         count = DUMPMAX;
         tag = "\n";
     } else {
         tag = "\"\n";
     }
-    while(count-- > 0){
-        if((*x >= ' ') && (*x < 127)) {
+    while (count-- > 0) {
+        if ((*x >= ' ') && (*x < 127)) {
             fputc(*x, stderr);
         } else {
             fputc('.', stderr);
@@ -254,8 +257,8 @@
                    << connection_str.length() << ")";
     }
 
-    memcpy(cp->data, connection_str.c_str(), connection_str.length());
-    cp->msg.data_length = connection_str.length();
+    cp->payload = std::move(connection_str);
+    cp->msg.data_length = cp->payload.size();
 
     send_packet(cp, t);
 }
@@ -323,15 +326,10 @@
 }
 
 static void handle_new_connection(atransport* t, apacket* p) {
-    if (t->GetConnectionState() != kCsOffline) {
-        t->SetConnectionState(kCsOffline);
-        handle_offline(t);
-    }
+    handle_offline(t);
 
     t->update_version(p->msg.arg0, p->msg.arg1);
-    std::string banner(reinterpret_cast<const char*>(p->data),
-                       p->msg.data_length);
-    parse_banner(banner, t);
+    parse_banner(p->payload, t);
 
 #if ADB_HOST
     handle_online(t);
@@ -354,21 +352,9 @@
             ((char*) (&(p->msg.command)))[2],
             ((char*) (&(p->msg.command)))[3]);
     print_packet("recv", p);
+    CHECK_EQ(p->payload.size(), p->msg.data_length);
 
     switch(p->msg.command){
-    case A_SYNC:
-        if (p->msg.arg0){
-            send_packet(p, t);
-#if ADB_HOST
-            send_connect(t);
-#endif
-        } else {
-            t->SetConnectionState(kCsOffline);
-            handle_offline(t);
-            send_packet(p, t);
-        }
-        return;
-
     case A_CNXN:  // CONNECT(version, maxdata, "system-id-string")
         handle_new_connection(t, p);
         break;
@@ -380,11 +366,11 @@
                 if (t->GetConnectionState() == kCsOffline) {
                     t->SetConnectionState(kCsUnauthorized);
                 }
-                send_auth_response(p->data, p->msg.data_length, t);
+                send_auth_response(p->payload.data(), p->msg.data_length, t);
                 break;
 #else
             case ADB_AUTH_SIGNATURE:
-                if (adbd_auth_verify(t->token, sizeof(t->token), p->data, p->msg.data_length)) {
+                if (adbd_auth_verify(t->token, sizeof(t->token), p->payload)) {
                     adbd_auth_verified(t);
                     t->failed_auth_attempts = 0;
                 } else {
@@ -394,7 +380,7 @@
                 break;
 
             case ADB_AUTH_RSAPUBLICKEY:
-                adbd_auth_confirm_key(p->data, p->msg.data_length, t);
+                adbd_auth_confirm_key(p->payload.data(), p->msg.data_length, t);
                 break;
 #endif
             default:
@@ -406,9 +392,7 @@
 
     case A_OPEN: /* OPEN(local-id, 0, "destination") */
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
-            char *name = (char*) p->data;
-            name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
-            asocket* s = create_local_service_socket(name, t);
+            asocket* s = create_local_service_socket(p->payload.c_str(), t);
             if (s == nullptr) {
                 send_close(0, p->msg.arg0, t);
             } else {
@@ -474,11 +458,7 @@
             asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
             if (s) {
                 unsigned rid = p->msg.arg0;
-
-                // TODO: Convert apacket::data to a type that we can move out of.
-                std::string copy(p->data, p->data + p->msg.data_length);
-
-                if (s->enqueue(s, std::move(copy)) == 0) {
+                if (s->enqueue(s, std::move(p->payload)) == 0) {
                     D("Enqueue the socket");
                     send_ready(s->id, rid, t);
                 }
@@ -1071,7 +1051,7 @@
         SendOkay(reply_fd);
 
         // Rely on process exit to close the socket for us.
-        android::base::quick_exit(0);
+        exit(0);
     }
 
     // "transport:" is used for switching transport with a specified serial number
diff --git a/adb/adb.h b/adb/adb.h
index c9c635a..c74fa99 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -74,7 +74,7 @@
 
 struct apacket {
     amessage msg;
-    char data[MAX_PAYLOAD];
+    std::string payload;
 };
 
 uint32_t calculate_apacket_checksum(const apacket* packet);
@@ -196,10 +196,6 @@
 
 extern const char* adb_device_banner;
 
-#if !ADB_HOST
-extern int SHELL_EXIT_NOTIFY_FD;
-#endif  // !ADB_HOST
-
 #define CHUNK_SIZE (64 * 1024)
 
 #if !ADB_HOST
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index a6f224f..715e04f 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -49,7 +49,7 @@
 void adbd_auth_verified(atransport *t);
 
 void adbd_cloexec_auth_socket();
-bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len);
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig);
 void adbd_auth_confirm_key(const char* data, size_t len, atransport* t);
 
 void send_auth_request(atransport *t);
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index a142384..fecf452 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -29,6 +29,7 @@
 
 #include "socket_spec.h"
 #include "sysdeps.h"
+#include "sysdeps/memory.h"
 #include "transport.h"
 
 // A listener is an entity which binds to a local port and, upon receiving a connection on that
@@ -203,7 +204,7 @@
         }
     }
 
-    std::unique_ptr<alistener> listener(new alistener(local_name, connect_to));
+    auto listener = std::make_unique<alistener>(local_name, connect_to);
 
     int resolved = 0;
     listener->fd = socket_spec_listen(listener->local_name, error, &resolved);
diff --git a/adb/adb_client.cpp b/adb/client/adb_client.cpp
similarity index 100%
rename from adb/adb_client.cpp
rename to adb/client/adb_client.cpp
diff --git a/adb/adb_client.h b/adb/client/adb_client.h
similarity index 100%
rename from adb/adb_client.h
rename to adb/client/adb_client.h
diff --git a/adb/adb_auth_host.cpp b/adb/client/auth.cpp
similarity index 95%
rename from adb/adb_auth_host.cpp
rename to adb/client/auth.cpp
index 365bf77..c3aef16 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/client/auth.cpp
@@ -299,20 +299,25 @@
     return result;
 }
 
-static int adb_auth_sign(RSA* key, const char* token, size_t token_size, char* sig) {
+static std::string adb_auth_sign(RSA* key, const char* token, size_t token_size) {
     if (token_size != TOKEN_SIZE) {
         D("Unexpected token size %zd", token_size);
         return 0;
     }
 
+    std::string result;
+    result.resize(MAX_PAYLOAD);
+
     unsigned int len;
     if (!RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
-                  reinterpret_cast<uint8_t*>(sig), &len, key)) {
-        return 0;
+                  reinterpret_cast<uint8_t*>(&result[0]), &len, key)) {
+        return std::string();
     }
 
+    result.resize(len);
+
     D("adb_auth_sign len=%d", len);
-    return (int)len;
+    return result;
 }
 
 std::string adb_auth_get_userkey() {
@@ -446,13 +451,14 @@
     }
 
     apacket* p = get_apacket();
-    memcpy(p->data, key.c_str(), key.size() + 1);
-
     p->msg.command = A_AUTH;
     p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
 
+    p->payload = std::move(key);
+
     // adbd expects a null-terminated string.
-    p->msg.data_length = key.size() + 1;
+    p->payload.push_back('\0');
+    p->msg.data_length = p->payload.size();
     send_packet(p, t);
 }
 
@@ -467,8 +473,8 @@
     LOG(INFO) << "Calling send_auth_response";
     apacket* p = get_apacket();
 
-    int ret = adb_auth_sign(key.get(), token, token_size, p->data);
-    if (!ret) {
+    std::string result = adb_auth_sign(key.get(), token, token_size);
+    if (result.empty()) {
         D("Error signing the token");
         put_apacket(p);
         return;
@@ -476,6 +482,7 @@
 
     p->msg.command = A_AUTH;
     p->msg.arg0 = ADB_AUTH_SIGNATURE;
-    p->msg.data_length = ret;
+    p->payload = std::move(result);
+    p->msg.data_length = p->payload.size();
     send_packet(p, t);
 }
diff --git a/adb/bugreport.cpp b/adb/client/bugreport.cpp
similarity index 100%
rename from adb/bugreport.cpp
rename to adb/client/bugreport.cpp
diff --git a/adb/bugreport.h b/adb/client/bugreport.h
similarity index 100%
rename from adb/bugreport.h
rename to adb/client/bugreport.h
diff --git a/adb/commandline.cpp b/adb/client/commandline.cpp
similarity index 99%
rename from adb/commandline.cpp
rename to adb/client/commandline.cpp
index d126f52..34930c6 100644
--- a/adb/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -61,6 +61,7 @@
 #include "services.h"
 #include "shell_service.h"
 #include "sysdeps/chrono.h"
+#include "sysdeps/memory.h"
 
 static int install_app(int argc, const char** argv);
 static int install_multiple_app(int argc, const char** argv);
@@ -263,7 +264,7 @@
     char raw_buffer[BUFSIZ];
     char* buffer_ptr = raw_buffer;
     if (use_shell_protocol) {
-        protocol.reset(new ShellProtocol(fd));
+        protocol = std::make_unique<ShellProtocol>(fd);
         if (!protocol) {
             LOG(ERROR) << "failed to allocate memory for ShellProtocol object";
             return 1;
@@ -630,7 +631,7 @@
     args->raw_stdin = raw_stdin;
     args->escape_char = escape_char;
     if (use_shell_protocol) {
-        args->protocol.reset(new ShellProtocol(args->write_fd));
+        args->protocol = std::make_unique<ShellProtocol>(args->write_fd);
     }
 
     if (raw_stdin) stdin_raw_init();
diff --git a/adb/commandline.h b/adb/client/commandline.h
similarity index 100%
rename from adb/commandline.h
rename to adb/client/commandline.h
diff --git a/adb/console.cpp b/adb/client/console.cpp
similarity index 100%
rename from adb/console.cpp
rename to adb/client/console.cpp
diff --git a/adb/file_sync_client.cpp b/adb/client/file_sync_client.cpp
similarity index 100%
rename from adb/file_sync_client.cpp
rename to adb/client/file_sync_client.cpp
diff --git a/adb/line_printer.cpp b/adb/client/line_printer.cpp
similarity index 100%
rename from adb/line_printer.cpp
rename to adb/client/line_printer.cpp
diff --git a/adb/line_printer.h b/adb/client/line_printer.h
similarity index 100%
rename from adb/line_printer.h
rename to adb/client/line_printer.h
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index f0d0ce7..31cb853 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -28,7 +28,6 @@
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 
 #include "adb.h"
@@ -61,7 +60,7 @@
 static BOOL WINAPI ctrlc_handler(DWORD type) {
     // TODO: Consider trying to kill a starting up adb server (if we're in
     // launch_server) by calling GenerateConsoleCtrlEvent().
-    android::base::quick_exit(STATUS_CONTROL_C_EXIT);
+    exit(STATUS_CONTROL_C_EXIT);
     return TRUE;
 }
 #endif
@@ -76,6 +75,12 @@
     usb_cleanup();
 }
 
+static void intentionally_leak() {
+    void* p = ::operator new(1);
+    // The analyzer is upset about this leaking. NOLINTNEXTLINE
+    LOG(INFO) << "leaking pointer " << p;
+}
+
 int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
 #if defined(_WIN32)
     // adb start-server starts us up with stdout and stderr hooked up to
@@ -95,16 +100,21 @@
     SetConsoleCtrlHandler(ctrlc_handler, TRUE);
 #else
     signal(SIGINT, [](int) {
-        fdevent_run_on_main_thread([]() { android::base::quick_exit(0); });
+        fdevent_run_on_main_thread([]() { exit(0); });
     });
 #endif
 
+    char* leak = getenv("ADB_LEAK");
+    if (leak && strcmp(leak, "1") == 0) {
+        intentionally_leak();
+    }
+
     if (is_daemon) {
         close_stdin();
         setup_daemon_logging();
     }
 
-    android::base::at_quick_exit(adb_server_cleanup);
+    atexit(adb_server_cleanup);
 
     init_transport_registration();
     init_mdns_transport_discovery();
diff --git a/adb/transport_mdns.cpp b/adb/client/transport_mdns.cpp
similarity index 100%
rename from adb/transport_mdns.cpp
rename to adb/client/transport_mdns.cpp
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 18f585d..46c3f58 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -19,6 +19,7 @@
 #include "sysdeps.h"
 
 #include <stdint.h>
+#include <stdlib.h>
 
 #include <atomic>
 #include <chrono>
@@ -33,7 +34,6 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
diff --git a/adb/adbd_auth.cpp b/adb/daemon/auth.cpp
similarity index 97%
rename from adb/adbd_auth.cpp
rename to adb/daemon/auth.cpp
index 3488ad1..3fd2b31 100644
--- a/adb/adbd_auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -46,7 +46,7 @@
 
 bool auth_required = true;
 
-bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len) {
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig) {
     static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr };
 
     for (const auto& path : key_paths) {
@@ -80,7 +80,8 @@
 
                 bool verified =
                     (RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
-                                reinterpret_cast<const uint8_t*>(sig), sig_len, key) == 1);
+                                reinterpret_cast<const uint8_t*>(sig.c_str()), sig.size(),
+                                key) == 1);
                 RSA_free(key);
                 if (verified) return true;
             }
@@ -210,10 +211,10 @@
     }
 
     apacket* p = get_apacket();
-    memcpy(p->data, t->token, sizeof(t->token));
     p->msg.command = A_AUTH;
     p->msg.arg0 = ADB_AUTH_TOKEN;
     p->msg.data_length = sizeof(t->token);
+    p->payload.assign(t->token, t->token + sizeof(t->token));
     send_packet(p, t);
 }
 
diff --git a/adb/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
similarity index 100%
rename from adb/file_sync_service.cpp
rename to adb/daemon/file_sync_service.cpp
diff --git a/adb/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
similarity index 100%
rename from adb/framebuffer_service.cpp
rename to adb/daemon/framebuffer_service.cpp
diff --git a/adb/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
similarity index 98%
rename from adb/jdwp_service.cpp
rename to adb/daemon/jdwp_service.cpp
index 6f5396a..9761558 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -128,7 +128,7 @@
 static void jdwp_process_list_updated(void);
 
 struct JdwpProcess;
-static std::list<std::unique_ptr<JdwpProcess>> _jdwp_list;
+static auto& _jdwp_list = *new std::list<std::unique_ptr<JdwpProcess>>();
 
 struct JdwpProcess {
     explicit JdwpProcess(int socket) {
@@ -511,7 +511,7 @@
     bool need_initial;
 };
 
-static std::vector<std::unique_ptr<JdwpTracker>> _jdwp_trackers;
+static auto& _jdwp_trackers = *new std::vector<std::unique_ptr<JdwpTracker>>();
 
 static void jdwp_process_list_updated(void) {
     std::string data;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 3c27582..4314dae 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -158,9 +158,10 @@
     // descriptor will always be open.
     adbd_cloexec_auth_socket();
 
-    if (ALLOW_ADBD_NO_AUTH && !android::base::GetBoolProperty("ro.adb.secure", false)) {
-        auth_required = false;
-    }
+#if defined(ALLOW_ADBD_NO_AUTH)
+    // If ro.adb.secure is unset, default to no authentication required.
+    auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+#endif
 
     adbd_auth_init();
 
diff --git a/adb/remount_service.cpp b/adb/daemon/remount_service.cpp
similarity index 79%
rename from adb/remount_service.cpp
rename to adb/daemon/remount_service.cpp
index 32ed28f..a4c7a5a 100644
--- a/adb/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -62,9 +62,9 @@
 
 // The proc entry for / is full of lies, so check fstab instead.
 // /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
-static std::string find_mount(const char* dir) {
-    if (strcmp(dir, "/") == 0) {
-       return find_fstab_mount(dir);
+static std::string find_mount(const char* dir, bool is_root) {
+    if (is_root) {
+        return find_fstab_mount(dir);
     } else {
        return find_proc_mount(dir);
     }
@@ -86,17 +86,29 @@
     if (!directory_exists(dir)) {
         return true;
     }
-    std::string dev = find_mount(dir);
-    if (dev.empty()) {
+    bool is_root = strcmp(dir, "/") == 0;
+    std::string dev = find_mount(dir, is_root);
+    // Even if the device for the root is not found, we still try to remount it
+    // as rw. This typically only happens when running Android in a container:
+    // the root will almost always be in a loop device, which is dynamic, so
+    // it's not convenient to put in the fstab.
+    if (dev.empty() && !is_root) {
         return true;
     }
-    if (!make_block_device_writable(dev)) {
+    if (!dev.empty() && !make_block_device_writable(dev)) {
         WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
                    dir, dev.c_str(), strerror(errno));
         return false;
     }
+    if (mount(dev.c_str(), dir, "none", MS_REMOUNT | MS_BIND, nullptr) == -1) {
+        // This is useful for cases where the superblock is already marked as
+        // read-write, but the mount itself is read-only, such as containers
+        // where the remount with just MS_REMOUNT is forbidden by the kernel.
+        WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
+        return false;
+    }
     if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
-        WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno));
+        WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
         return false;
     }
     return true;
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
similarity index 94%
rename from adb/set_verity_enable_state_service.cpp
rename to adb/daemon/set_verity_enable_state_service.cpp
index 49e0363..0fcf89b 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -98,13 +98,22 @@
     return android::base::GetProperty("ro.boot.slot_suffix", "");
 }
 
+static bool is_avb_device_locked() {
+    return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
+}
+
 /* Use AVB to turn verity on/off */
 static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
     std::string ab_suffix = get_ab_suffix();
-
     bool verity_enabled;
+
+    if (is_avb_device_locked()) {
+        WriteFdFmt(fd, "Device is locked. Please unlock the device first\n");
+        return false;
+    }
+
     if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
-        WriteFdFmt(fd, "Error getting verity state\n");
+        WriteFdFmt(fd, "Error getting verity state. Try adb root first?\n");
         return false;
     }
 
diff --git a/adb/shell_service.cpp b/adb/daemon/shell_service.cpp
similarity index 96%
rename from adb/shell_service.cpp
rename to adb/daemon/shell_service.cpp
index f9f80c0..401c99c 100644
--- a/adb/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -334,8 +334,18 @@
         // processes, so we need to manually reset back to SIG_DFL here (http://b/35209888).
         signal(SIGPIPE, SIG_DFL);
 
+        // Increase oom_score_adj from -1000, so that the child is visible to the OOM-killer.
+        // Don't treat failure as an error, because old Android kernels explicitly disabled this.
+        int oom_score_adj_fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
+        if (oom_score_adj_fd != -1) {
+            const char* oom_score_adj_value = "-950";
+            TEMP_FAILURE_RETRY(
+                adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
+        }
+
         if (command_.empty()) {
-            execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
+            // Spawn a login shell if we don't have a command.
+            execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
         } else {
             execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
         }
@@ -372,8 +382,8 @@
         }
         D("protocol FD = %d", protocol_sfd_.get());
 
-        input_.reset(new ShellProtocol(protocol_sfd_));
-        output_.reset(new ShellProtocol(protocol_sfd_));
+        input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+        output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
         if (!input_ || !output_) {
             *error = "failed to allocate shell protocol objects";
             kill(pid_, SIGKILL);
@@ -681,22 +691,6 @@
         }
         protocol_sfd_.reset(-1);
     }
-
-    // Pass the local socket FD to the shell cleanup fdevent.
-    if (SHELL_EXIT_NOTIFY_FD >= 0) {
-        int fd = local_socket_sfd_;
-        if (WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd))) {
-            D("passed fd %d to SHELL_EXIT_NOTIFY_FD (%d) for pid %d",
-              fd, SHELL_EXIT_NOTIFY_FD, pid_);
-            // The shell exit fdevent now owns the FD and will close it once
-            // the last bit of data flushes through.
-            static_cast<void>(local_socket_sfd_.release());
-        } else {
-            PLOG(ERROR) << "failed to write fd " << fd
-                        << " to SHELL_EXIT_NOTIFY_FD (" << SHELL_EXIT_NOTIFY_FD
-                        << ") for pid " << pid_;
-        }
-    }
 }
 
 }  // namespace
diff --git a/adb/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
similarity index 91%
rename from adb/shell_service_test.cpp
rename to adb/daemon/shell_service_test.cpp
index 839284e..4e27822 100644
--- a/adb/shell_service_test.cpp
+++ b/adb/daemon/shell_service_test.cpp
@@ -55,40 +55,20 @@
     static sighandler_t saved_sigpipe_handler_;
 
     int subprocess_fd_ = -1;
-    int shell_exit_receiver_fd_ = -1, saved_shell_exit_fd_;
 };
 
 sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
 
 void ShellServiceTest::StartTestSubprocess(
         const char* command, SubprocessType type, SubprocessProtocol protocol) {
-    // We want to intercept the shell exit message to make sure it's sent.
-    saved_shell_exit_fd_ = SHELL_EXIT_NOTIFY_FD;
-    int fd[2];
-    ASSERT_TRUE(adb_socketpair(fd) >= 0);
-    SHELL_EXIT_NOTIFY_FD = fd[0];
-    shell_exit_receiver_fd_ = fd[1];
-
     subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
     ASSERT_TRUE(subprocess_fd_ >= 0);
 }
 
 void ShellServiceTest::CleanupTestSubprocess() {
     if (subprocess_fd_ >= 0) {
-        // Subprocess should send its FD to SHELL_EXIT_NOTIFY_FD for cleanup.
-        int notified_fd = -1;
-        ASSERT_TRUE(ReadFdExactly(shell_exit_receiver_fd_, &notified_fd,
-                                  sizeof(notified_fd)));
-        ASSERT_EQ(notified_fd, subprocess_fd_);
-
         adb_close(subprocess_fd_);
         subprocess_fd_ = -1;
-
-        // Restore SHELL_EXIT_NOTIFY_FD.
-        adb_close(SHELL_EXIT_NOTIFY_FD);
-        adb_close(shell_exit_receiver_fd_);
-        shell_exit_receiver_fd_ = -1;
-        SHELL_EXIT_NOTIFY_FD = saved_shell_exit_fd_;
     }
 }
 
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 87ed3db..ab11bdd 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -58,10 +58,6 @@
 #define cpu_to_le16(x) htole16(x)
 #define cpu_to_le32(x) htole32(x)
 
-#define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
-
-static constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
-
 static int dummy_fd = -1;
 
 struct func_desc {
@@ -256,7 +252,6 @@
     ssize_t ret;
     struct desc_v1 v1_descriptor;
     struct desc_v2 v2_descriptor;
-    size_t retries = 0;
 
     v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
     v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
@@ -274,7 +269,7 @@
 
     if (h->control < 0) { // might have already done this before
         LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
-        h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
+        h->control = adb_open(USB_FFS_ADB_EP0, O_WRONLY);
         if (h->control < 0) {
             PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
             goto err;
@@ -305,18 +300,20 @@
         android::base::SetProperty("sys.usb.ffs.ready", "1");
     }
 
-    h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
+    h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDONLY);
     if (h->bulk_out < 0) {
         PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
         goto err;
     }
 
-    h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
+    h->bulk_in = adb_open(USB_FFS_ADB_IN, O_WRONLY);
     if (h->bulk_in < 0) {
         PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
         goto err;
     }
 
+    memset(&h->read_aiob.ctx, 0, sizeof(h->read_aiob.ctx));
+    memset(&h->write_aiob.ctx, 0, sizeof(h->write_aiob.ctx));
     if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
         io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
         D("[ aio: got error on io_setup (%d) ]", errno);
@@ -324,30 +321,6 @@
 
     h->read_aiob.fd = h->bulk_out;
     h->write_aiob.fd = h->bulk_in;
-
-    h->max_rw = MAX_PAYLOAD;
-    while (h->max_rw >= USB_FFS_BULK_SIZE && retries < ENDPOINT_ALLOC_RETRIES) {
-        int ret_in = ioctl(h->bulk_in, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
-        int errno_in = errno;
-        int ret_out = ioctl(h->bulk_out, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
-        int errno_out = errno;
-
-        if (ret_in || ret_out) {
-            if (errno_in == ENODEV || errno_out == ENODEV) {
-                std::this_thread::sleep_for(100ms);
-                retries += 1;
-                continue;
-            }
-            h->max_rw /= 2;
-        } else {
-            return true;
-        }
-    }
-
-    D("[ adb: cannot call endpoint alloc: errno=%d ]", errno);
-    // Kernel pre-allocation could have failed for recoverable reasons.
-    // Continue running with a safe max rw size.
-    h->max_rw = USB_FFS_BULK_SIZE;
     return true;
 
 err:
@@ -401,7 +374,7 @@
 
     const char* buf = static_cast<const char*>(data);
     while (len > 0) {
-        int write_len = std::min(h->max_rw, len);
+        int write_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_write(h->bulk_in, buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
@@ -420,7 +393,7 @@
 
     char* buf = static_cast<char*>(data);
     while (len > 0) {
-        int read_len = std::min(h->max_rw, len);
+        int read_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_read(h->bulk_out, buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
index db1a6d6..15a7f65 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/usb.h
@@ -54,7 +54,5 @@
     // read and write threads.
     struct aio_block read_aiob;
     struct aio_block write_aiob;
-
-    int max_rw;
 };
 
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index b28de4b..9776c1b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,11 +21,13 @@
 #include "fdevent.h"
 
 #include <fcntl.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <atomic>
+#include <deque>
 #include <functional>
 #include <list>
 #include <mutex>
@@ -35,19 +37,13 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
+#include <android-base/threads.h>
 
 #include "adb_io.h"
 #include "adb_trace.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 
-#if !ADB_HOST
-// This socket is used when a subproc shell service exists.
-// It wakes up the fdevent_loop() and cause the correct handling
-// of the shell's pseudo-tty master. I.e. force close it.
-int SHELL_EXIT_NOTIFY_FD = -1;
-#endif // !ADB_HOST
-
 #define FDE_EVENTMASK  0x00ff
 #define FDE_STATEMASK  0xff00
 
@@ -77,21 +73,21 @@
 static auto& g_pending_list = *new std::list<fdevent*>();
 static std::atomic<bool> terminate_loop(false);
 static bool main_thread_valid;
-static unsigned long main_thread_id;
+static uint64_t main_thread_id;
 
 static auto& run_queue_notify_fd = *new unique_fd();
 static auto& run_queue_mutex = *new std::mutex();
-static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::deque<std::function<void()>>();
 
 void check_main_thread() {
     if (main_thread_valid) {
-        CHECK_EQ(main_thread_id, adb_thread_id());
+        CHECK_EQ(main_thread_id, android::base::GetThreadId());
     }
 }
 
 void set_main_thread() {
     main_thread_valid = true;
-    main_thread_id = adb_thread_id();
+    main_thread_id = android::base::GetThreadId();
 }
 
 static std::string dump_fde(const fdevent* fde) {
@@ -293,79 +289,23 @@
     fde->func(fde->fd, events, fde->arg);
 }
 
-#if !ADB_HOST
-
-#include <sys/ioctl.h>
-
-static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
-    D("subproc handling on fd = %d, ev = %x", fd, ev);
-
-    CHECK_GE(fd, 0);
-
-    if (ev & FDE_READ) {
-        int subproc_fd;
-
-        if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
-            LOG(FATAL) << "Failed to read the subproc's fd from " << fd;
+static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
+    // We need to be careful around reentrancy here, since a function we call can queue up another
+    // function.
+    while (true) {
+        std::function<void()> fn;
+        {
+            std::lock_guard<std::mutex> lock(run_queue_mutex);
+            if (run_queue.empty()) {
+                break;
+            }
+            fn = run_queue.front();
+            run_queue.pop_front();
         }
-        auto it = g_poll_node_map.find(subproc_fd);
-        if (it == g_poll_node_map.end()) {
-            D("subproc_fd %d cleared from fd_table", subproc_fd);
-            adb_close(subproc_fd);
-            return;
-        }
-        fdevent* subproc_fde = it->second.fde;
-        if(subproc_fde->fd != subproc_fd) {
-            // Already reallocated?
-            LOG(FATAL) << "subproc_fd(" << subproc_fd << ") != subproc_fde->fd(" << subproc_fde->fd
-                       << ")";
-            return;
-        }
-
-        subproc_fde->force_eof = 1;
-
-        int rcount = 0;
-        ioctl(subproc_fd, FIONREAD, &rcount);
-        D("subproc with fd %d has rcount=%d, err=%d", subproc_fd, rcount, errno);
-        if (rcount != 0) {
-            // If there is data left, it will show up in the select().
-            // This works because there is no other thread reading that
-            // data when in this fd_func().
-            return;
-        }
-
-        D("subproc_fde %s", dump_fde(subproc_fde).c_str());
-        subproc_fde->events |= FDE_READ;
-        if(subproc_fde->state & FDE_PENDING) {
-            return;
-        }
-        subproc_fde->state |= FDE_PENDING;
-        fdevent_call_fdfunc(subproc_fde);
+        fn();
     }
 }
 
-static void fdevent_subproc_setup() {
-    int s[2];
-
-    if(adb_socketpair(s)) {
-        PLOG(FATAL) << "cannot create shell-exit socket-pair";
-    }
-    D("fdevent_subproc: socket pair (%d, %d)", s[0], s[1]);
-
-    SHELL_EXIT_NOTIFY_FD = s[0];
-    fdevent *fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
-    CHECK(fde != nullptr) << "cannot create fdevent for shell-exit handler";
-    fdevent_add(fde, FDE_READ);
-}
-#endif // !ADB_HOST
-
-static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
-    for (auto& f : run_queue) {
-        f();
-    }
-    run_queue.clear();
-}
-
 static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
     CHECK_GE(fd, 0);
     CHECK(ev & FDE_READ);
@@ -377,22 +317,27 @@
         PLOG(FATAL) << "failed to empty run queue notify fd";
     }
 
-    std::lock_guard<std::mutex> lock(run_queue_mutex);
     fdevent_run_flush();
 }
 
 static void fdevent_run_setup() {
-    std::lock_guard<std::mutex> lock(run_queue_mutex);
-    CHECK(run_queue_notify_fd.get() == -1);
-    int s[2];
-    if (adb_socketpair(s) != 0) {
-        PLOG(FATAL) << "failed to create run queue notify socketpair";
-    }
+    {
+        std::lock_guard<std::mutex> lock(run_queue_mutex);
+        CHECK(run_queue_notify_fd.get() == -1);
+        int s[2];
+        if (adb_socketpair(s) != 0) {
+            PLOG(FATAL) << "failed to create run queue notify socketpair";
+        }
 
-    run_queue_notify_fd.reset(s[0]);
-    fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
-    CHECK(fde != nullptr);
-    fdevent_add(fde, FDE_READ);
+        if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
+            PLOG(FATAL) << "failed to make run queue notify socket nonblocking";
+        }
+
+        run_queue_notify_fd.reset(s[0]);
+        fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+        CHECK(fde != nullptr);
+        fdevent_add(fde, FDE_READ);
+    }
 
     fdevent_run_flush();
 }
@@ -404,7 +349,12 @@
     // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
     // In that case, rely on the setup code to flush the queue without a notification being needed.
     if (run_queue_notify_fd != -1) {
-        if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+        int rc = adb_write(run_queue_notify_fd.get(), "", 1);
+
+        // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
+        if (rc == 0) {
+            PLOG(FATAL) << "run queue notify fd was closed?";
+        } else if (rc == -1 && errno != EAGAIN) {
             PLOG(FATAL) << "failed to write to run queue notify fd";
         }
     }
@@ -412,9 +362,6 @@
 
 void fdevent_loop() {
     set_main_thread();
-#if !ADB_HOST
-    fdevent_subproc_setup();
-#endif // !ADB_HOST
     fdevent_run_setup();
 
     while (true) {
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 86e0209..e3d5a35 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -26,6 +26,7 @@
 
 #include "adb_io.h"
 #include "fdevent_test.h"
+#include "sysdeps/memory.h"
 
 class FdHandler {
   public:
@@ -99,7 +100,7 @@
 
     std::vector<std::unique_ptr<FdHandler>> fd_handlers;
     for (size_t i = 0; i < read_fds.size(); ++i) {
-        fd_handlers.push_back(std::unique_ptr<FdHandler>(new FdHandler(read_fds[i], write_fds[i])));
+        fd_handlers.push_back(std::make_unique<FdHandler>(read_fds[i], write_fds[i]));
     }
 
     fdevent_loop();
@@ -180,7 +181,13 @@
     PrepareThread();
     std::thread thread(fdevent_loop);
 
-    for (int i = 0; i < 100; ++i) {
+    // Block the main thread for a long time while we queue our callbacks.
+    fdevent_run_on_main_thread([]() {
+        check_main_thread();
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+    });
+
+    for (int i = 0; i < 1000000; ++i) {
         fdevent_run_on_main_thread([i, &vec]() {
             check_main_thread();
             vec.push_back(i);
@@ -189,6 +196,34 @@
 
     TerminateThread(thread);
 
+    ASSERT_EQ(1000000u, vec.size());
+    for (int i = 0; i < 1000000; ++i) {
+        ASSERT_EQ(i, vec[i]);
+    }
+}
+
+static std::function<void()> make_appender(std::vector<int>* vec, int value) {
+    return [vec, value]() {
+        check_main_thread();
+        if (value == 100) {
+            return;
+        }
+
+        vec->push_back(value);
+        fdevent_run_on_main_thread(make_appender(vec, value + 1));
+    };
+}
+
+TEST_F(FdeventTest, run_on_main_thread_reentrant) {
+    std::vector<int> vec;
+
+    PrepareThread();
+    std::thread thread(fdevent_loop);
+
+    fdevent_run_on_main_thread(make_appender(&vec, 0));
+
+    TerminateThread(thread);
+
     ASSERT_EQ(100u, vec.size());
     for (int i = 0; i < 100; ++i) {
         ASSERT_EQ(i, vec[i]);
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index 5ca49ac..1a2d41c 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -52,13 +52,8 @@
     }
 
     size_t GetAdditionalLocalSocketCount() {
-#if ADB_HOST
         // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
         return 2;
-#else
-        // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
-        return 3;
-#endif
     }
 
     void TerminateThread(std::thread& thread) {
diff --git a/adb/protocol.txt b/adb/protocol.txt
index 55ea87f..f4523c4 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -183,9 +183,11 @@
 
 Command constant: A_SYNC
 
-The SYNC message is used by the io pump to make sure that stale
+*** obsolete, no longer used ***
+
+The SYNC message was used by the io pump to make sure that stale
 outbound messages are discarded when the connection to the remote side
-is broken.  It is only used internally to the bridge and never valid
+is broken.  It was only used internally to the bridge and never valid
 to send across the wire.
 
 * when the connection to the remote side goes offline, the io pump
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 04ad6f3..6b40056 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -113,16 +113,12 @@
     asocket* s = create_local_socket(arg->socket_fd);
     ASSERT_TRUE(s != nullptr);
     arg->bytes_written = 0;
-    while (true) {
-        std::string data;
-        data.resize(MAX_PAYLOAD);
-        arg->bytes_written += data.size();
-        int ret = s->enqueue(s, std::move(data));
-        if (ret == 1) {
-            // The writer has one packet waiting to send.
-            break;
-        }
-    }
+
+    std::string data;
+    data.resize(MAX_PAYLOAD);
+    arg->bytes_written += data.size();
+    int ret = s->enqueue(s, std::move(data));
+    ASSERT_EQ(1, ret);
 
     asocket* cause_close_s = create_local_socket(arg->cause_close_fd);
     ASSERT_TRUE(cause_close_s != nullptr);
@@ -213,6 +209,45 @@
     TerminateThread(thread);
 }
 
+// Ensure that if we fail to write output to an fd, we will still flush data coming from it.
+TEST_F(LocalSocketTest, flush_after_shutdown) {
+    int head_fd[2];
+    int tail_fd[2];
+    ASSERT_EQ(0, adb_socketpair(head_fd));
+    ASSERT_EQ(0, adb_socketpair(tail_fd));
+
+    asocket* head = create_local_socket(head_fd[1]);
+    asocket* tail = create_local_socket(tail_fd[1]);
+
+    head->peer = tail;
+    head->ready(head);
+
+    tail->peer = head;
+    tail->ready(tail);
+
+    PrepareThread();
+    std::thread thread(fdevent_loop);
+
+    EXPECT_TRUE(WriteFdExactly(head_fd[0], "foo", 3));
+
+    EXPECT_EQ(0, adb_shutdown(head_fd[0], SHUT_RD));
+    const char* str = "write succeeds, but local_socket will fail to write";
+    EXPECT_TRUE(WriteFdExactly(tail_fd[0], str, strlen(str)));
+    EXPECT_TRUE(WriteFdExactly(head_fd[0], "bar", 3));
+
+    char buf[6];
+    EXPECT_TRUE(ReadFdExactly(tail_fd[0], buf, 6));
+    EXPECT_EQ(0, memcmp(buf, "foobar", 6));
+
+    adb_close(head_fd[0]);
+    adb_close(tail_fd[0]);
+
+    // Wait until the local sockets are closed.
+    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+    TerminateThread(thread);
+}
+
 #if defined(__linux__)
 
 static void ClientThreadFunc() {
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 307cbfe..e05a3db 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -106,50 +106,131 @@
     }
 }
 
+enum class SocketFlushResult {
+    Destroyed,
+    TryAgain,
+    Completed,
+};
+
+static SocketFlushResult local_socket_flush_incoming(asocket* s) {
+    while (!s->packet_queue.empty()) {
+        Range& r = s->packet_queue.front();
+
+        int rc = adb_write(s->fd, r.data(), r.size());
+        if (rc == static_cast<int>(r.size())) {
+            s->packet_queue.pop_front();
+        } else if (rc > 0) {
+            r.drop_front(rc);
+            fdevent_add(&s->fde, FDE_WRITE);
+            return SocketFlushResult::TryAgain;
+        } else if (rc == -1 && errno == EAGAIN) {
+            fdevent_add(&s->fde, FDE_WRITE);
+            return SocketFlushResult::TryAgain;
+        }
+
+        // We failed to write, but it's possible that we can still read from the socket.
+        // Give that a try before giving up.
+        s->has_write_error = true;
+        break;
+    }
+
+    // If we sent the last packet of a closing socket, we can now destroy it.
+    if (s->closing) {
+        s->close(s);
+        return SocketFlushResult::Destroyed;
+    }
+
+    fdevent_del(&s->fde, FDE_WRITE);
+    return SocketFlushResult::Completed;
+}
+
+// Returns false if the socket has been closed and destroyed as a side-effect of this function.
+static bool local_socket_flush_outgoing(asocket* s) {
+    const size_t max_payload = s->get_max_payload();
+    std::string data;
+    data.resize(max_payload);
+    char* x = &data[0];
+    size_t avail = max_payload;
+    int r = 0;
+    int is_eof = 0;
+
+    while (avail > 0) {
+        r = adb_read(s->fd, x, avail);
+        D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu", s->id, s->fd, r,
+          r < 0 ? errno : 0, avail);
+        if (r == -1) {
+            if (errno == EAGAIN) {
+                break;
+            }
+        } else if (r > 0) {
+            avail -= r;
+            x += r;
+            continue;
+        }
+
+        /* r = 0 or unhandled error */
+        is_eof = 1;
+        break;
+    }
+    D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof,
+      s->fde.force_eof);
+
+    if (avail != max_payload && s->peer) {
+        data.resize(max_payload - avail);
+
+        // s->peer->enqueue() may call s->close() and free s,
+        // so save variables for debug printing below.
+        unsigned saved_id = s->id;
+        int saved_fd = s->fd;
+        r = s->peer->enqueue(s->peer, std::move(data));
+        D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
+
+        if (r < 0) {
+            // Error return means they closed us as a side-effect and we must
+            // return immediately.
+            //
+            // Note that if we still have buffered packets, the socket will be
+            // placed on the closing socket list. This handler function will be
+            // called again to process FDE_WRITE events.
+            return false;
+        }
+
+        if (r > 0) {
+            /* if the remote cannot accept further events,
+            ** we disable notification of READs.  They'll
+            ** be enabled again when we get a call to ready()
+            */
+            fdevent_del(&s->fde, FDE_READ);
+        }
+    }
+
+    // Don't allow a forced eof if data is still there.
+    if ((s->fde.force_eof && !r) || is_eof) {
+        D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde.force_eof);
+        s->close(s);
+        return false;
+    }
+
+    return true;
+}
+
 static int local_socket_enqueue(asocket* s, std::string data) {
     D("LS(%d): enqueue %zu", s->id, data.size());
 
     Range r(std::move(data));
-
-    /* if there is already data queue'd, we will receive
-    ** events when it's time to write.  just add this to
-    ** the tail
-    */
-    if (!s->packet_queue.empty()) {
-        goto enqueue;
-    }
-
-    /* write as much as we can, until we
-    ** would block or there is an error/eof
-    */
-    while (!r.empty()) {
-        int rc = adb_write(s->fd, r.data(), r.size());
-        if (rc > 0) {
-            r.drop_front(rc);
-            continue;
-        }
-
-        if (rc == 0 || errno != EAGAIN) {
-            D("LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno));
-            s->has_write_error = true;
-            s->close(s);
-            return 1; /* not ready (error) */
-        } else {
-            // errno == EAGAIN
-            break;
-        }
-    }
-
-    if (r.empty()) {
-        return 0; /* ready for more data */
-    }
-
-enqueue:
-    /* make sure we are notified when we can drain the queue */
     s->packet_queue.push_back(std::move(r));
-    fdevent_add(&s->fde, FDE_WRITE);
+    switch (local_socket_flush_incoming(s)) {
+        case SocketFlushResult::Destroyed:
+            return -1;
 
-    return 1; /* not ready (backlog) */
+        case SocketFlushResult::TryAgain:
+            return 1;
+
+        case SocketFlushResult::Completed:
+            return 0;
+    }
+
+    return !s->packet_queue.empty();
 }
 
 static void local_socket_ready(asocket* s) {
@@ -224,114 +305,21 @@
     ** in order to simplify the code.
     */
     if (ev & FDE_WRITE) {
-        while (!s->packet_queue.empty()) {
-            Range& r = s->packet_queue.front();
-            while (!r.empty()) {
-                int rc = adb_write(fd, r.data(), r.size());
-                if (rc == -1) {
-                    /* returning here is ok because FDE_READ will
-                    ** be processed in the next iteration loop
-                    */
-                    if (errno == EAGAIN) {
-                        return;
-                    }
-                } else if (rc > 0) {
-                    r.drop_front(rc);
-                    continue;
-                }
-
-                D(" closing after write because rc=%d and errno is %d", rc, errno);
-                s->has_write_error = true;
-                s->close(s);
+        switch (local_socket_flush_incoming(s)) {
+            case SocketFlushResult::Destroyed:
                 return;
-            }
 
-            if (r.empty()) {
-                s->packet_queue.pop_front();
-            }
+            case SocketFlushResult::TryAgain:
+                break;
+
+            case SocketFlushResult::Completed:
+                s->peer->ready(s->peer);
+                break;
         }
-
-        /* if we sent the last packet of a closing socket,
-        ** we can now destroy it.
-        */
-        if (s->closing) {
-            D(" closing because 'closing' is set after write");
-            s->close(s);
-            return;
-        }
-
-        /* no more packets queued, so we can ignore
-        ** writable events again and tell our peer
-        ** to resume writing
-        */
-        fdevent_del(&s->fde, FDE_WRITE);
-        s->peer->ready(s->peer);
     }
 
     if (ev & FDE_READ) {
-        const size_t max_payload = s->get_max_payload();
-        std::string data;
-        data.resize(max_payload);
-        char* x = &data[0];
-        size_t avail = max_payload;
-        int r = 0;
-        int is_eof = 0;
-
-        while (avail > 0) {
-            r = adb_read(fd, x, avail);
-            D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu", s->id, s->fd, r,
-              r < 0 ? errno : 0, avail);
-            if (r == -1) {
-                if (errno == EAGAIN) {
-                    break;
-                }
-            } else if (r > 0) {
-                avail -= r;
-                x += r;
-                continue;
-            }
-
-            /* r = 0 or unhandled error */
-            is_eof = 1;
-            break;
-        }
-        D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof,
-          s->fde.force_eof);
-
-        if (avail != max_payload && s->peer) {
-            data.resize(max_payload - avail);
-
-            // s->peer->enqueue() may call s->close() and free s,
-            // so save variables for debug printing below.
-            unsigned saved_id = s->id;
-            int saved_fd = s->fd;
-            r = s->peer->enqueue(s->peer, std::move(data));
-            D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
-
-            if (r < 0) {
-                /* error return means they closed us as a side-effect
-                ** and we must return immediately.
-                **
-                ** note that if we still have buffered packets, the
-                ** socket will be placed on the closing socket list.
-                ** this handler function will be called again
-                ** to process FDE_WRITE events.
-                */
-                return;
-            }
-
-            if (r > 0) {
-                /* if the remote cannot accept further events,
-                ** we disable notification of READs.  They'll
-                ** be enabled again when we get a call to ready()
-                */
-                fdevent_del(&s->fde, FDE_READ);
-            }
-        }
-        /* Don't allow a forced eof if data is still there */
-        if ((s->fde.force_eof && !r) || is_eof) {
-            D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde.force_eof);
-            s->close(s);
+        if (!local_socket_flush_outgoing(s)) {
             return;
         }
     }
@@ -413,15 +401,15 @@
     p->msg.command = A_WRTE;
     p->msg.arg0 = s->peer->id;
     p->msg.arg1 = s->id;
-    p->msg.data_length = data.size();
 
-    if (data.size() > sizeof(p->data)) {
+    if (data.size() > MAX_PAYLOAD) {
         put_apacket(p);
         return -1;
     }
 
-    // TODO: Convert apacket::data to a type that we can move into.
-    memcpy(p->data, data.data(), data.size());
+    p->payload = std::move(data);
+    p->msg.data_length = p->payload.size();
+
     send_packet(p, s->transport);
     return 1;
 }
@@ -482,17 +470,20 @@
 void connect_to_remote(asocket* s, const char* destination) {
     D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
     apacket* p = get_apacket();
-    size_t len = strlen(destination) + 1;
-
-    if (len > (s->get_max_payload() - 1)) {
-        fatal("destination oversized");
-    }
 
     D("LS(%d): connect('%s')", s->id, destination);
     p->msg.command = A_OPEN;
     p->msg.arg0 = s->id;
-    p->msg.data_length = len;
-    strcpy((char*)p->data, destination);
+
+    // adbd expects a null-terminated string.
+    p->payload = destination;
+    p->payload.push_back('\0');
+    p->msg.data_length = p->payload.size();
+
+    if (p->msg.data_length > s->get_max_payload()) {
+        fatal("destination oversized");
+    }
+
     send_packet(p, s->transport);
 }
 
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 307be6d..3be99f6 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -92,11 +92,6 @@
     return 0;
 }
 
-static __inline__  unsigned long adb_thread_id()
-{
-    return GetCurrentThreadId();
-}
-
 static __inline__ void  close_on_exec(int  fd)
 {
     /* nothing really */
@@ -111,14 +106,14 @@
 #define  mkdir  ___xxx_mkdir
 
 // See the comments for the !defined(_WIN32) versions of adb_*().
-extern int  adb_open(const char*  path, int  options);
-extern int  adb_creat(const char*  path, int  mode);
-extern int  adb_read(int  fd, void* buf, int len);
-extern int  adb_write(int  fd, const void*  buf, int  len);
-extern int  adb_lseek(int  fd, int  pos, int  where);
-extern int  adb_shutdown(int  fd);
-extern int  adb_close(int  fd);
-extern int  adb_register_socket(SOCKET s);
+extern int adb_open(const char* path, int options);
+extern int adb_creat(const char* path, int mode);
+extern int adb_read(int fd, void* buf, int len);
+extern int adb_write(int fd, const void* buf, int len);
+extern int adb_lseek(int fd, int pos, int where);
+extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
+extern int adb_close(int fd);
+extern int adb_register_socket(SOCKET s);
 
 // See the comments for the !defined(_WIN32) version of unix_close().
 static __inline__ int  unix_close(int fd)
@@ -419,14 +414,10 @@
 #undef   open
 #define  open    ___xxx_open
 
-static __inline__ int  adb_shutdown(int fd)
-{
-    return shutdown(fd, SHUT_RDWR);
-}
-static __inline__ int  adb_shutdown(int fd, int direction)
-{
+static __inline__ int adb_shutdown(int fd, int direction = SHUT_RDWR) {
     return shutdown(fd, direction);
 }
+
 #undef   shutdown
 #define  shutdown   ____xxx_shutdown
 
@@ -625,11 +616,6 @@
     return path[0] == '/';
 }
 
-static __inline__ unsigned long adb_thread_id()
-{
-    return (unsigned long)gettid();
-}
-
 #endif /* !_WIN32 */
 
 static inline void disable_tcp_nagle(int fd) {
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
new file mode 100644
index 0000000..0e4c509
--- /dev/null
+++ b/adb/sysdeps/memory.h
@@ -0,0 +1,36 @@
+#pragma once
+
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <type_traits>
+
+#if defined(_WIN32)
+// We don't have C++14 on Windows yet.
+// Reimplement std::make_unique ourselves until we do.
+
+namespace std {
+
+template <typename T, typename... Args>
+typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(
+    Args&&... args) {
+    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+}  // namespace std
+
+#endif
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index 45da5af..ecd1fd2 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -105,8 +105,7 @@
     }
 
     if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
-        // Arbitrarily selected value, ported from libcutils.
-        if (listen(s, 4) != 0) {
+        if (listen(s, SOMAXCONN) != 0) {
             set_error(error);
             return -1;
         }
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 5873b2b..62f4ac8 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -747,8 +747,6 @@
     return fd;
 }
 
-#define LISTEN_BACKLOG 4
-
 // interface_address is INADDR_LOOPBACK or INADDR_ANY.
 static int _network_server(int port, int type, u_long interface_address, std::string* error) {
     struct sockaddr_in addr;
@@ -805,7 +803,7 @@
         return -1;
     }
     if (type == SOCK_STREAM) {
-        if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
+        if (listen(s, SOMAXCONN) == SOCKET_ERROR) {
             const DWORD err = WSAGetLastError();
             *error = android::base::StringPrintf(
                 "cannot listen on socket: %s", android::base::SystemErrorCodeToString(err).c_str());
@@ -1013,9 +1011,8 @@
     return ntohs(reinterpret_cast<sockaddr_in*>(&addr_storage)->sin_port);
 }
 
-int  adb_shutdown(int  fd)
-{
-    FH   f = _fh_from_int(fd, __func__);
+int adb_shutdown(int fd, int direction) {
+    FH f = _fh_from_int(fd, __func__);
 
     if (!f || f->clazz != &_fh_socket_class) {
         D("adb_shutdown: invalid fd %d", fd);
@@ -1023,8 +1020,8 @@
         return -1;
     }
 
-    D( "adb_shutdown: %s", f->name);
-    if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+    D("adb_shutdown: %s", f->name);
+    if (shutdown(f->fh_socket, direction) == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
         D("socket shutdown fd %d failed: %s", fd,
           android::base::SystemErrorCodeToString(err).c_str());
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 98c8a59..e771106 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -217,8 +217,12 @@
         ipv4.listen(1)
 
         ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
-        ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
-        ipv6.listen(1)
+        try:
+            ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
+            ipv6.listen(1)
+        except socket.error:
+            print("IPv6 not available, skipping")
+            return
 
         for s in (ipv4, ipv6):
             port = s.getsockname()[1]
diff --git a/adb/test_device.py b/adb/test_device.py
index 4cf2206..d39eb14 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -31,6 +31,7 @@
 import subprocess
 import sys
 import tempfile
+import threading
 import time
 import unittest
 
@@ -493,6 +494,29 @@
         stdout, _ = self.device.shell(["cat", log_path])
         self.assertEqual(stdout.strip(), "SIGHUP")
 
+    def test_exit_stress(self):
+        """Hammer `adb shell exit 42` with multiple threads."""
+        thread_count = 48
+        result = dict()
+        def hammer(thread_idx, thread_count, result):
+            success = True
+            for i in range(thread_idx, 240, thread_count):
+                ret = subprocess.call(['adb', 'shell', 'exit {}'.format(i)])
+                if ret != i % 256:
+                    success = False
+                    break
+            result[thread_idx] = success
+
+        threads = []
+        for i in range(thread_count):
+            thread = threading.Thread(target=hammer, args=(i, thread_count, result))
+            thread.start()
+            threads.append(thread)
+        for thread in threads:
+            thread.join()
+        for i, success in result.iteritems():
+            self.assertTrue(success)
+
 
 class ArgumentEscapingTest(DeviceTest):
     def test_shell_escaping(self):
@@ -1163,7 +1187,7 @@
         # Verify that the device ended up with the expected UTF-8 path
         output = self.device.shell(
                 ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
-        self.assertEqual(remote_path.encode('utf-8'), output)
+        self.assertEqual(remote_path, output)
 
         # pull.
         self.device.pull(remote_path, tf.name)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 9ae1297..37b56e2 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -17,6 +17,8 @@
 #define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
+#include "sysdeps/memory.h"
+
 #include "transport.h"
 
 #include <ctype.h>
@@ -28,23 +30,24 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <deque>
 #include <list>
 #include <mutex>
 #include <thread>
 
 #include <android-base/logging.h>
 #include <android-base/parsenetaddress.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/thread_annotations.h>
 
+#include <diagnose_usb.h>
+
 #include "adb.h"
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_trace.h"
 #include "adb_utils.h"
-#include "diagnose_usb.h"
 #include "fdevent.h"
 
 static void transport_unref(atransport *t);
@@ -66,18 +69,96 @@
     return next++;
 }
 
+BlockingConnectionAdapter::BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection)
+    : underlying_(std::move(connection)) {}
+
+BlockingConnectionAdapter::~BlockingConnectionAdapter() {
+    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): destructing";
+    Stop();
+}
+
+void BlockingConnectionAdapter::Start() {
+    read_thread_ = std::thread([this]() {
+        LOG(INFO) << this->transport_name_ << ": read thread spawning";
+        while (true) {
+            auto packet = std::make_unique<apacket>();
+            if (!underlying_->Read(packet.get())) {
+                PLOG(INFO) << this->transport_name_ << ": read failed";
+                break;
+            }
+            read_callback_(this, std::move(packet));
+        }
+        std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "read failed"); });
+    });
+
+    write_thread_ = std::thread([this]() {
+        LOG(INFO) << this->transport_name_ << ": write thread spawning";
+        while (true) {
+            std::unique_lock<std::mutex> lock(mutex_);
+            cv_.wait(lock, [this]() { return this->stopped_ || !this->write_queue_.empty(); });
+
+            if (this->stopped_) {
+                return;
+            }
+
+            std::unique_ptr<apacket> packet = std::move(this->write_queue_.front());
+            this->write_queue_.pop_front();
+            lock.unlock();
+
+            if (!this->underlying_->Write(packet.get())) {
+                break;
+            }
+        }
+        std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "write failed"); });
+    });
+}
+
+void BlockingConnectionAdapter::Stop() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    if (stopped_) {
+        LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): already stopped";
+        return;
+    }
+
+    stopped_ = true;
+    lock.unlock();
+
+    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopping";
+
+    this->underlying_->Close();
+
+    this->cv_.notify_one();
+    read_thread_.join();
+    write_thread_.join();
+
+    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopped";
+    std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "requested stop"); });
+}
+
+bool BlockingConnectionAdapter::Write(std::unique_ptr<apacket> packet) {
+    {
+        std::unique_lock<std::mutex> lock(this->mutex_);
+        write_queue_.emplace_back(std::move(packet));
+    }
+
+    cv_.notify_one();
+    return true;
+}
+
 bool FdConnection::Read(apacket* packet) {
     if (!ReadFdExactly(fd_.get(), &packet->msg, sizeof(amessage))) {
         D("remote local: read terminated (message)");
         return false;
     }
 
-    if (packet->msg.data_length > sizeof(packet->data)) {
+    if (packet->msg.data_length > MAX_PAYLOAD) {
         D("remote local: read overflow (data length = %" PRIu32 ")", packet->msg.data_length);
         return false;
     }
 
-    if (!ReadFdExactly(fd_.get(), &packet->data, packet->msg.data_length)) {
+    packet->payload.resize(packet->msg.data_length);
+
+    if (!ReadFdExactly(fd_.get(), &packet->payload[0], packet->payload.size())) {
         D("remote local: terminated (data)");
         return false;
     }
@@ -86,13 +167,18 @@
 }
 
 bool FdConnection::Write(apacket* packet) {
-    uint32_t length = packet->msg.data_length;
-
-    if (!WriteFdExactly(fd_.get(), &packet->msg, sizeof(amessage) + length)) {
+    if (!WriteFdExactly(fd_.get(), &packet->msg, sizeof(packet->msg))) {
         D("remote local: write terminated");
         return false;
     }
 
+    if (packet->msg.data_length) {
+        if (!WriteFdExactly(fd_.get(), &packet->payload[0], packet->msg.data_length)) {
+            D("remote local: write terminated");
+            return false;
+        }
+    }
+
     return true;
 }
 
@@ -133,70 +219,10 @@
 
     std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
                                                      func, cmd, arg0, arg1, len);
-    result += dump_hex(p->data, len);
+    result += dump_hex(p->payload.data(), p->payload.size());
     return result;
 }
 
-static int read_packet(int fd, const char* name, apacket** ppacket) {
-    ATRACE_NAME("read_packet");
-    char buff[8];
-    if (!name) {
-        snprintf(buff, sizeof buff, "fd=%d", fd);
-        name = buff;
-    }
-    char* p = reinterpret_cast<char*>(ppacket); /* really read a packet address */
-    int len = sizeof(apacket*);
-    while (len > 0) {
-        int r = adb_read(fd, p, len);
-        if (r > 0) {
-            len -= r;
-            p += r;
-        } else {
-            D("%s: read_packet (fd=%d), error ret=%d: %s", name, fd, r, strerror(errno));
-            return -1;
-        }
-    }
-
-    VLOG(TRANSPORT) << dump_packet(name, "from remote", *ppacket);
-    return 0;
-}
-
-static int write_packet(int fd, const char* name, apacket** ppacket) {
-    ATRACE_NAME("write_packet");
-    char buff[8];
-    if (!name) {
-        snprintf(buff, sizeof buff, "fd=%d", fd);
-        name = buff;
-    }
-    VLOG(TRANSPORT) << dump_packet(name, "to remote", *ppacket);
-    char* p = reinterpret_cast<char*>(ppacket); /* we really write the packet address */
-    int len = sizeof(apacket*);
-    while (len > 0) {
-        int r = adb_write(fd, p, len);
-        if (r > 0) {
-            len -= r;
-            p += r;
-        } else {
-            D("%s: write_packet (fd=%d) error ret=%d: %s", name, fd, r, strerror(errno));
-            return -1;
-        }
-    }
-    return 0;
-}
-
-static void transport_socket_events(int fd, unsigned events, void* _t) {
-    atransport* t = reinterpret_cast<atransport*>(_t);
-    D("transport_socket_events(fd=%d, events=%04x,...)", fd, events);
-    if (events & FDE_READ) {
-        apacket* p = 0;
-        if (read_packet(fd, t->serial, &p)) {
-            D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
-        } else {
-            handle_packet(p, (atransport*)_t);
-        }
-    }
-}
-
 void send_packet(apacket* p, atransport* t) {
     p->msg.magic = p->msg.command ^ 0xffffffff;
     // compute a checksum for connection/auth packets for compatibility reasons
@@ -206,154 +232,18 @@
         p->msg.data_check = calculate_apacket_checksum(p);
     }
 
-    print_packet("send", p);
+    VLOG(TRANSPORT) << dump_packet(t->serial, "to remote", p);
 
     if (t == NULL) {
         fatal("Transport is null");
     }
 
-    if (write_packet(t->transport_socket, t->serial, &p)) {
-        fatal_errno("cannot enqueue packet on transport socket");
+    if (t->Write(p) != 0) {
+        D("%s: failed to enqueue packet, closing transport", t->serial);
+        t->Kick();
     }
 }
 
-// The transport is opened by transport_register_func before
-// the read_transport and write_transport threads are started.
-//
-// The read_transport thread issues a SYNC(1, token) message to let
-// the write_transport thread know to start things up.  In the event
-// of transport IO failure, the read_transport thread will post a
-// SYNC(0,0) message to ensure shutdown.
-//
-// The transport will not actually be closed until both threads exit, but the threads
-// will kick the transport on their way out to disconnect the underlying device.
-//
-// read_transport thread reads data from a transport (representing a usb/tcp connection),
-// and makes the main thread call handle_packet().
-static void read_transport_thread(void* _t) {
-    atransport* t = reinterpret_cast<atransport*>(_t);
-    apacket* p;
-
-    adb_thread_setname(
-        android::base::StringPrintf("<-%s", (t->serial != nullptr ? t->serial : "transport")));
-    D("%s: starting read_transport thread on fd %d, SYNC online (%d)", t->serial, t->fd,
-      t->sync_token + 1);
-    p = get_apacket();
-    p->msg.command = A_SYNC;
-    p->msg.arg0 = 1;
-    p->msg.arg1 = ++(t->sync_token);
-    p->msg.magic = A_SYNC ^ 0xffffffff;
-    if (write_packet(t->fd, t->serial, &p)) {
-        put_apacket(p);
-        D("%s: failed to write SYNC packet", t->serial);
-        goto oops;
-    }
-
-    D("%s: data pump started", t->serial);
-    for (;;) {
-        ATRACE_NAME("read_transport loop");
-        p = get_apacket();
-
-        {
-            ATRACE_NAME("read_transport read_remote");
-            if (!t->connection->Read(p)) {
-                D("%s: remote read failed for transport", t->serial);
-                put_apacket(p);
-                break;
-            }
-
-            if (!check_header(p, t)) {
-                D("%s: remote read: bad header", t->serial);
-                put_apacket(p);
-                break;
-            }
-
-#if ADB_HOST
-            if (p->msg.command == 0) {
-                put_apacket(p);
-                continue;
-            }
-#endif
-        }
-
-        D("%s: received remote packet, sending to transport", t->serial);
-        if (write_packet(t->fd, t->serial, &p)) {
-            put_apacket(p);
-            D("%s: failed to write apacket to transport", t->serial);
-            goto oops;
-        }
-    }
-
-    D("%s: SYNC offline for transport", t->serial);
-    p = get_apacket();
-    p->msg.command = A_SYNC;
-    p->msg.arg0 = 0;
-    p->msg.arg1 = 0;
-    p->msg.magic = A_SYNC ^ 0xffffffff;
-    if (write_packet(t->fd, t->serial, &p)) {
-        put_apacket(p);
-        D("%s: failed to write SYNC apacket to transport", t->serial);
-    }
-
-oops:
-    D("%s: read_transport thread is exiting", t->serial);
-    kick_transport(t);
-    transport_unref(t);
-}
-
-// write_transport thread gets packets sent by the main thread (through send_packet()),
-// and writes to a transport (representing a usb/tcp connection).
-static void write_transport_thread(void* _t) {
-    atransport* t = reinterpret_cast<atransport*>(_t);
-    apacket* p;
-    int active = 0;
-
-    adb_thread_setname(
-        android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport")));
-    D("%s: starting write_transport thread, reading from fd %d", t->serial, t->fd);
-
-    for (;;) {
-        ATRACE_NAME("write_transport loop");
-        if (read_packet(t->fd, t->serial, &p)) {
-            D("%s: failed to read apacket from transport on fd %d", t->serial, t->fd);
-            break;
-        }
-
-        if (p->msg.command == A_SYNC) {
-            if (p->msg.arg0 == 0) {
-                D("%s: transport SYNC offline", t->serial);
-                put_apacket(p);
-                break;
-            } else {
-                if (p->msg.arg1 == t->sync_token) {
-                    D("%s: transport SYNC online", t->serial);
-                    active = 1;
-                } else {
-                    D("%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token);
-                }
-            }
-        } else {
-            if (active) {
-                D("%s: transport got packet, sending to remote", t->serial);
-                ATRACE_NAME("write_transport write_remote");
-                if (t->Write(p) != 0) {
-                    D("%s: remote write failed for transport", t->serial);
-                    put_apacket(p);
-                    break;
-                }
-            } else {
-                D("%s: transport ignoring packet while offline", t->serial);
-            }
-        }
-
-        put_apacket(p);
-    }
-
-    D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
-    kick_transport(t);
-    transport_unref(t);
-}
-
 void kick_transport(atransport* t) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     // As kick_transport() can be called from threads without guarantee that t is valid,
@@ -544,9 +434,10 @@
     return 0;
 }
 
-static void transport_registration_func(int _fd, unsigned ev, void* data) {
+static void remove_transport(atransport*);
+
+static void transport_registration_func(int _fd, unsigned ev, void*) {
     tmsg m;
-    int s[2];
     atransport* t;
 
     if (!(ev & FDE_READ)) {
@@ -560,13 +451,7 @@
     t = m.transport;
 
     if (m.action == 0) {
-        D("transport: %s removing and free'ing %d", t->serial, t->transport_socket);
-
-        /* IMPORTANT: the remove closes one half of the
-        ** socket pair.  The close closes the other half.
-        */
-        fdevent_remove(&(t->transport_fde));
-        adb_close(t->fd);
+        D("transport: %s deleting", t->serial);
 
         {
             std::lock_guard<std::recursive_mutex> lock(transport_lock);
@@ -588,23 +473,33 @@
     /* don't create transport threads for inaccessible devices */
     if (t->GetConnectionState() != kCsNoPerm) {
         /* initial references are the two threads */
-        t->ref_count = 2;
+        t->ref_count = 1;
+        t->connection->SetTransportName(t->serial_name());
+        t->connection->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
+            if (!check_header(p.get(), t)) {
+                D("%s: remote read: bad header", t->serial);
+                return false;
+            }
 
-        if (adb_socketpair(s)) {
-            fatal_errno("cannot open transport socketpair");
-        }
+            VLOG(TRANSPORT) << dump_packet(t->serial, "from remote", p.get());
+            apacket* packet = p.release();
 
-        D("transport: %s socketpair: (%d,%d) starting", t->serial, s[0], s[1]);
+            // TODO: Does this need to run on the main thread?
+            fdevent_run_on_main_thread([packet, t]() { handle_packet(packet, t); });
+            return true;
+        });
+        t->connection->SetErrorCallback([t](Connection*, const std::string& error) {
+            D("%s: connection terminated: %s", t->serial, error.c_str());
+            fdevent_run_on_main_thread([t]() {
+                handle_offline(t);
+                transport_unref(t);
+            });
+        });
 
-        t->transport_socket = s[0];
-        t->fd = s[1];
-
-        fdevent_install(&(t->transport_fde), t->transport_socket, transport_socket_events, t);
-
-        fdevent_set(&(t->transport_fde), FDE_READ);
-
-        std::thread(write_transport_thread, t).detach();
-        std::thread(read_transport_thread, t).detach();
+        t->connection->Start();
+#if ADB_HOST
+        send_connect(t);
+#endif
     }
 
     {
@@ -670,7 +565,7 @@
     t->ref_count--;
     if (t->ref_count == 0) {
         D("transport: %s unref (kicking and closing)", t->serial);
-        t->connection->Close();
+        t->connection->Stop();
         remove_transport(t);
     } else {
         D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
@@ -721,9 +616,7 @@
     std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& t : transport_list) {
         if (t->GetConnectionState() == kCsNoPerm) {
-#if ADB_HOST
             *error_out = UsbNoPermissionsLongHelpText();
-#endif
             continue;
         }
 
@@ -798,14 +691,14 @@
 }
 
 int atransport::Write(apacket* p) {
-    return this->connection->Write(p) ? 0 : -1;
+    return this->connection->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
 }
 
 void atransport::Kick() {
     if (!kicked_) {
         D("kicking transport %s", this->serial);
         kicked_ = true;
-        this->connection->Close();
+        this->connection->Stop();
     }
 }
 
@@ -818,7 +711,7 @@
     connection_state_ = state;
 }
 
-const std::string atransport::connection_state_name() const {
+std::string atransport::connection_state_name() const {
     ConnectionState state = GetConnectionState();
     switch (state) {
         case kCsOffline:
diff --git a/adb/transport.h b/adb/transport.h
index 9700f44..8b71e34 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -20,12 +20,14 @@
 #include <sys/types.h>
 
 #include <atomic>
+#include <condition_variable>
 #include <deque>
 #include <functional>
 #include <list>
 #include <memory>
 #include <mutex>
 #include <string>
+#include <thread>
 #include <unordered_set>
 
 #include <openssl/rsa.h>
@@ -57,15 +59,47 @@
 
 TransportId NextTransportId();
 
-// Abstraction for a blocking packet transport.
+// Abstraction for a non-blocking packet transport.
 struct Connection {
     Connection() = default;
-    Connection(const Connection& copy) = delete;
-    Connection(Connection&& move) = delete;
-
-    // Destroy a Connection. Formerly known as 'Close' in atransport.
     virtual ~Connection() = default;
 
+    void SetTransportName(std::string transport_name) {
+        transport_name_ = std::move(transport_name);
+    }
+
+    using ReadCallback = std::function<bool(Connection*, std::unique_ptr<apacket>)>;
+    void SetReadCallback(ReadCallback callback) {
+        CHECK(!read_callback_);
+        read_callback_ = callback;
+    }
+
+    // Called after the Connection has terminated, either by an error or because Stop was called.
+    using ErrorCallback = std::function<void(Connection*, const std::string&)>;
+    void SetErrorCallback(ErrorCallback callback) {
+        CHECK(!error_callback_);
+        error_callback_ = callback;
+    }
+
+    virtual bool Write(std::unique_ptr<apacket> packet) = 0;
+
+    virtual void Start() = 0;
+    virtual void Stop() = 0;
+
+    std::string transport_name_;
+    ReadCallback read_callback_;
+    ErrorCallback error_callback_;
+};
+
+// Abstraction for a blocking packet transport.
+struct BlockingConnection {
+    BlockingConnection() = default;
+    BlockingConnection(const BlockingConnection& copy) = delete;
+    BlockingConnection(BlockingConnection&& move) = delete;
+
+    // Destroy a BlockingConnection. Formerly known as 'Close' in atransport.
+    virtual ~BlockingConnection() = default;
+
     // Read/Write a packet. These functions are concurrently called from a transport's reader/writer
     // threads.
     virtual bool Read(apacket* packet) = 0;
@@ -77,7 +111,30 @@
     virtual void Close() = 0;
 };
 
-struct FdConnection : public Connection {
+struct BlockingConnectionAdapter : public Connection {
+    explicit BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection);
+
+    virtual ~BlockingConnectionAdapter();
+
+    virtual bool Write(std::unique_ptr<apacket> packet) override final;
+
+    virtual void Start() override final;
+    virtual void Stop() override final;
+
+    bool stopped_ = false;
+
+    std::unique_ptr<BlockingConnection> underlying_;
+    std::thread read_thread_;
+    std::thread write_thread_;
+
+    std::deque<std::unique_ptr<apacket>> write_queue_;
+    std::mutex mutex_;
+    std::condition_variable cv_;
+
+    std::once_flag error_flag_;
+};
+
+struct FdConnection : public BlockingConnection {
     explicit FdConnection(unique_fd fd) : fd_(std::move(fd)) {}
 
     bool Read(apacket* packet) override final;
@@ -89,7 +146,7 @@
     unique_fd fd_;
 };
 
-struct UsbConnection : public Connection {
+struct UsbConnection : public BlockingConnection {
     explicit UsbConnection(usb_handle* handle) : handle_(handle) {}
     ~UsbConnection();
 
@@ -110,7 +167,6 @@
 
     atransport(ConnectionState state = kCsOffline)
         : id(NextTransportId()), connection_state_(state) {
-        transport_fde = {};
         // Initialize protocol to min version for compatibility with older versions.
         // Version will be updated post-connect.
         protocol_version = A_VERSION_MIN;
@@ -126,11 +182,7 @@
     void SetConnectionState(ConnectionState state);
 
     const TransportId id;
-    int fd = -1;
-    int transport_socket = -1;
-    fdevent transport_fde;
     size_t ref_count = 0;
-    uint32_t sync_token = 0;
     bool online = false;
     TransportType type = kTransportAny;
 
@@ -152,8 +204,8 @@
     char token[TOKEN_SIZE] = {};
     size_t failed_auth_attempts = 0;
 
-    const std::string serial_name() const { return serial ? serial : "<unknown>"; }
-    const std::string connection_state_name() const;
+    std::string serial_name() const { return serial ? serial : "<unknown>"; }
+    std::string connection_state_name() const;
 
     void update_version(int version, size_t payload);
     int get_protocol_version() const;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 560a031..c09fcb7 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -45,6 +45,7 @@
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "sysdeps/chrono.h"
+#include "sysdeps/memory.h"
 
 #if ADB_HOST
 
@@ -445,13 +446,13 @@
     int fail = 0;
 
     unique_fd fd(s);
-    t->sync_token = 1;
     t->type = kTransportLocal;
 
 #if ADB_HOST
     // Emulator connection.
     if (local) {
-        t->connection.reset(new EmulatorConnection(std::move(fd), adb_port));
+        auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
+        t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection));
         std::lock_guard<std::mutex> lock(local_transports_lock);
         atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
         if (existing_transport != NULL) {
@@ -470,6 +471,7 @@
 #endif
 
     // Regular tcp connection.
-    t->connection.reset(new FdConnection(std::move(fd)));
+    auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
+    t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
     return fail;
 }
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index a108699..e9a75cd 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -17,6 +17,7 @@
 #define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
+#include "sysdeps/memory.h"
 #include "transport.h"
 
 #include <stdio.h>
@@ -61,13 +62,12 @@
 static int UsbReadPayload(usb_handle* h, apacket* p) {
     D("UsbReadPayload(%d)", p->msg.data_length);
 
-    if (p->msg.data_length > sizeof(p->data)) {
+    if (p->msg.data_length > MAX_PAYLOAD) {
         return -1;
     }
 
 #if CHECK_PACKET_OVERFLOW
     size_t usb_packet_size = usb_get_max_packet_size(h);
-    CHECK_EQ(0ULL, sizeof(p->data) % usb_packet_size);
 
     // Round the data length up to the nearest packet size boundary.
     // The device won't send a zero packet for packet size aligned payloads,
@@ -77,10 +77,18 @@
     if (rem_size) {
         len += usb_packet_size - rem_size;
     }
-    CHECK_LE(len, sizeof(p->data));
-    return usb_read(h, &p->data, len);
+
+    p->payload.resize(len);
+    int rc = usb_read(h, &p->payload[0], p->payload.size());
+    if (rc != static_cast<int>(p->msg.data_length)) {
+        return -1;
+    }
+
+    p->payload.resize(rc);
+    return rc;
 #else
-    return usb_read(h, &p->data, p->msg.data_length);
+    p->payload.resize(p->msg.data_length);
+    return usb_read(h, &p->payload[0], p->payload.size());
 #endif
 }
 
@@ -120,12 +128,13 @@
     }
 
     if (p->msg.data_length) {
-        if (p->msg.data_length > sizeof(p->data)) {
+        if (p->msg.data_length > MAX_PAYLOAD) {
             PLOG(ERROR) << "remote usb: read overflow (data length = " << p->msg.data_length << ")";
             return -1;
         }
 
-        if (usb_read(usb, p->data, p->msg.data_length)) {
+        p->payload.resize(p->msg.data_length);
+        if (usb_read(usb, &p->payload[0], p->payload.size())) {
             PLOG(ERROR) << "remote usb: terminated (data)";
             return -1;
         }
@@ -152,7 +161,7 @@
         return false;
     }
 
-    if (packet->msg.data_length != 0 && usb_write(handle_, &packet->data, size) != 0) {
+    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != 0) {
         PLOG(ERROR) << "remote usb: 2 - write terminated";
         return false;
     }
@@ -166,8 +175,8 @@
 
 void init_usb_transport(atransport* t, usb_handle* h) {
     D("transport: usb");
-    t->connection.reset(new UsbConnection(h));
-    t->sync_token = 1;
+    auto connection = std::make_unique<UsbConnection>(h);
+    t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(connection));
     t->type = kTransportUsb;
 }
 
diff --git a/base/Android.bp b/base/Android.bp
index 5d70d47..7b0ba11 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -50,6 +50,7 @@
         "quick_exit.cpp",
         "stringprintf.cpp",
         "strings.cpp",
+        "threads.cpp",
         "test_utils.cpp",
     ],
 
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index afff2c9..cc7aaf6 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -105,6 +105,9 @@
 
 void DefaultAborter(const char* abort_message);
 
+std::string GetDefaultTag();
+void SetDefaultTag(const std::string& tag);
+
 #ifdef __ANDROID__
 // We expose this even though it is the default because a user that wants to
 // override the default log buffer will have to construct this themselves.
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index abcf4bc..c314e02 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -17,20 +17,27 @@
 #ifndef ANDROID_BASE_SCOPEGUARD_H
 #define ANDROID_BASE_SCOPEGUARD_H
 
-#include <utility>  // for std::move
+#include <utility>  // for std::move, std::forward
 
 namespace android {
 namespace base {
 
+// ScopeGuard ensures that the specified functor is executed no matter how the
+// current scope exits.
 template <typename F>
 class ScopeGuard {
  public:
-  ScopeGuard(F f) : f_(f), active_(true) {}
+  ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
 
   ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
     that.active_ = false;
   }
 
+  template <typename Functor>
+  ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
+    that.active_ = false;
+  }
+
   ~ScopeGuard() {
     if (active_) f_();
   }
@@ -45,13 +52,16 @@
   bool active() const { return active_; }
 
  private:
+  template <typename Functor>
+  friend class ScopeGuard;
+
   F f_;
   bool active_;
 };
 
-template <typename T>
-ScopeGuard<T> make_scope_guard(T f) {
-  return ScopeGuard<T>(f);
+template <typename F>
+ScopeGuard<F> make_scope_guard(F&& f) {
+  return ScopeGuard<F>(std::forward<F>(f));
 }
 
 }  // namespace base
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index c11acb1..4d9fa34 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -67,8 +67,8 @@
 // TODO: string_view
 bool EndsWith(const std::string& s, const char* suffix);
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
-bool EndsWith(const std::string& s, const std::string& prefix);
-bool EndsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool EndsWith(const std::string& s, const std::string& suffix);
+bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
 
 // Tests whether 'lhs' equals 'rhs', ignoring case.
 bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 2edafe3..b95fa07 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -31,6 +31,8 @@
   // Release the ownership of fd, caller is reponsible for closing the
   // fd or stream properly.
   int release();
+  // Don't remove the temporary file in the destructor.
+  void DoNotRemove() { remove_file_ = false; }
 
   int fd;
   char path[1024];
@@ -38,6 +40,8 @@
  private:
   void init(const std::string& tmp_dir);
 
+  bool remove_file_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
 };
 
diff --git a/adb/transport_mdns_unsupported.cpp b/base/include/android-base/threads.h
similarity index 70%
rename from adb/transport_mdns_unsupported.cpp
rename to base/include/android-base/threads.h
index 387d341..85e65ba 100644
--- a/adb/transport_mdns_unsupported.cpp
+++ b/base/include/android-base/threads.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,5 +14,15 @@
  * limitations under the License.
  */
 
-/* For when mDNS discovery is unsupported */
-void init_mdns_transport_discovery(void) {}
+#ifndef ANDROID_BASE_THREADS_H
+#define ANDROID_BASE_THREADS_H
+
+#include <stdint.h>
+
+namespace android {
+namespace base {
+uint64_t GetThreadId();
+}
+}  // namespace android
+
+#endif
diff --git a/base/logging.cpp b/base/logging.cpp
index 1f7bc2a..30d7f8d 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -21,6 +21,7 @@
 #include "android-base/logging.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <libgen.h>
 #include <time.h>
 
@@ -54,41 +55,7 @@
 
 #include <android-base/macros.h>
 #include <android-base/strings.h>
-
-// For gettid.
-#if defined(__APPLE__)
-#include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <unistd.h>
-#elif defined(__linux__) && !defined(__ANDROID__)
-#include <syscall.h>
-#include <unistd.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-#if defined(_WIN32)
-typedef uint32_t thread_id;
-#else
-typedef pid_t thread_id;
-#endif
-
-static thread_id GetThreadId() {
-#if defined(__BIONIC__)
-  return gettid();
-#elif defined(__APPLE__)
-  uint64_t tid;
-  pthread_threadid_np(NULL, &tid);
-  return tid;
-#elif defined(__linux__)
-  return syscall(__NR_gettid);
-#elif defined(_WIN32)
-  return GetCurrentThreadId();
-#endif
-}
+#include <android-base/threads.h>
 
 namespace {
 #if defined(__GLIBC__)
@@ -139,9 +106,27 @@
   return aborter;
 }
 
-static std::string& ProgramInvocationName() {
-  static auto& programInvocationName = *new std::string(getprogname());
-  return programInvocationName;
+static std::recursive_mutex& TagLock() {
+  static auto& tag_lock = *new std::recursive_mutex();
+  return tag_lock;
+}
+static std::string* gDefaultTag;
+std::string GetDefaultTag() {
+  std::lock_guard<std::recursive_mutex> lock(TagLock());
+  if (gDefaultTag == nullptr) {
+    return "";
+  }
+  return *gDefaultTag;
+}
+void SetDefaultTag(const std::string& tag) {
+  std::lock_guard<std::recursive_mutex> lock(TagLock());
+  if (gDefaultTag != nullptr) {
+    delete gDefaultTag;
+    gDefaultTag = nullptr;
+  }
+  if (!tag.empty()) {
+    gDefaultTag = new std::string(tag);
+  }
 }
 
 static bool gInitialized = false;
@@ -205,8 +190,8 @@
   static_assert(arraysize(log_characters) - 1 == FATAL + 1,
                 "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
-  fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", tag ? tag : "nullptr", severity_char, timestamp,
-          getpid(), GetThreadId(), file, line, message);
+  fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
+          timestamp, getpid(), GetThreadId(), file, line, message);
 }
 
 void DefaultAborter(const char* abort_message) {
@@ -269,8 +254,7 @@
   // Linux to recover this, but we don't have that luxury on the Mac/Windows,
   // and there are a couple of argv[0] variants that are commonly used.
   if (argv != nullptr) {
-    std::lock_guard<std::mutex> lock(LoggingLock());
-    ProgramInvocationName() = basename(argv[0]);
+    SetDefaultTag(basename(argv[0]));
   }
 
   const char* tags = getenv("ANDROID_LOG_TAGS");
@@ -448,8 +432,15 @@
 
 void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
                          const char* tag, const char* message) {
-  if (tag == nullptr) tag = ProgramInvocationName().c_str();
-  Logger()(id, severity, tag, file, line, message);
+  if (tag == nullptr) {
+    std::lock_guard<std::recursive_mutex> lock(TagLock());
+    if (gDefaultTag == nullptr) {
+      gDefaultTag = new std::string(getprogname());
+    }
+    Logger()(id, severity, gDefaultTag->c_str(), file, line, message);
+  } else {
+    Logger()(id, severity, tag, file, line, message);
+  }
 }
 
 void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 6f05d9b..5f689fa 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -206,8 +206,8 @@
 }
 #endif
 
-static void CheckMessage(const CapturedStderr& cap,
-                         android::base::LogSeverity severity, const char* expected) {
+static void CheckMessage(const CapturedStderr& cap, android::base::LogSeverity severity,
+                         const char* expected, const char* expected_tag = nullptr) {
   std::string output;
   ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
   android::base::ReadFdToString(cap.fd(), &output);
@@ -217,9 +217,18 @@
   // many characters are in the log message.
   ASSERT_GT(output.length(), strlen(expected));
   ASSERT_NE(nullptr, strstr(output.c_str(), expected)) << output;
+  if (expected_tag != nullptr) {
+    ASSERT_NE(nullptr, strstr(output.c_str(), expected_tag)) << output;
+  }
 
 #if !defined(_WIN32)
-  std::regex message_regex(make_log_pattern(severity, expected));
+  std::string regex_str;
+  if (expected_tag != nullptr) {
+    regex_str.append(expected_tag);
+    regex_str.append(" ");
+  }
+  regex_str.append(make_log_pattern(severity, expected));
+  std::regex message_regex(regex_str);
   ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
 #endif
 }
@@ -600,3 +609,17 @@
 __attribute__((constructor)) void TestLoggingInConstructor() {
   LOG(ERROR) << "foobar";
 }
+
+TEST(logging, SetDefaultTag) {
+  constexpr const char* expected_tag = "test_tag";
+  constexpr const char* expected_msg = "foobar";
+  CapturedStderr cap;
+  {
+    std::string old_default_tag = android::base::GetDefaultTag();
+    android::base::SetDefaultTag(expected_tag);
+    android::base::ScopedLogSeverity sls(android::base::LogSeverity::INFO);
+    LOG(INFO) << expected_msg;
+    android::base::SetDefaultTag(old_default_tag);
+  }
+  CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag);
+}
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
index e11154a..9236d7b 100644
--- a/base/scopeguard_test.cpp
+++ b/base/scopeguard_test.cpp
@@ -17,6 +17,7 @@
 #include "android-base/scopeguard.h"
 
 #include <utility>
+#include <vector>
 
 #include <gtest/gtest.h>
 
@@ -44,3 +45,15 @@
   EXPECT_FALSE(scopeguard.active());
   ASSERT_FALSE(guarded_var);
 }
+
+TEST(scopeguard, vector) {
+  int guarded_var = 0;
+  {
+    std::vector<android::base::ScopeGuard<std::function<void()>>> scopeguards;
+    scopeguards.emplace_back(android::base::make_scope_guard(
+        std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
+    scopeguards.emplace_back(android::base::make_scope_guard(
+        std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
+  }
+  ASSERT_EQ(guarded_var, 2);
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 9d8dfb2..1619c21 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -92,7 +92,9 @@
   if (fd != -1) {
     close(fd);
   }
-  unlink(path);
+  if (remove_file_) {
+    unlink(path);
+  }
 }
 
 int TemporaryFile::release() {
diff --git a/base/threads.cpp b/base/threads.cpp
new file mode 100644
index 0000000..a71382b
--- /dev/null
+++ b/base/threads.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/threads.h>
+
+#include <stdint.h>
+#include <unistd.h>
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+namespace android {
+namespace base {
+
+uint64_t GetThreadId() {
+#if defined(__BIONIC__)
+  return gettid();
+#elif defined(__APPLE__)
+  uint64_t tid;
+  pthread_threadid_np(NULL, &tid);
+  return tid;
+#elif defined(__linux__)
+  return syscall(__NR_gettid);
+#elif defined(_WIN32)
+  return GetCurrentThreadId();
+#endif
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 79702a6..b194bbe 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -239,6 +239,8 @@
   return 0
 }
 
+BAD_BOOTLOADER_REASON=
+
 [ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
 
 Returns true (0) if current return (regex) value is true and the result matches
@@ -249,9 +251,20 @@
   value="${2}"
   shift 2
   val=`adb shell getprop ${property} 2>&1`
-  EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
-    [ -n "${1}" ] ||
-    save_ret=${?}
+  EXPECT_EQ "${value}" "${val}" for Android property ${property}
+  local_ret=${?}
+  if [ 0 != ${local_ret} -a "ro.boot.bootreason" = "${property}" ]; then
+    if [ -z "${BAD_BOOTLOADER_REASON}" ]; then
+      BAD_BOOTLOADER_REASON=${val}
+    elif [ X"${BAD_BOOTLOADER_REASON}" = X"${val}" ]; then
+      local_ret=0
+    fi
+  fi
+  if [ 0 != ${local_ret} ]; then
+    if [ -z "${1}" ] ; then
+      save_ret=${local_ret}
+    fi
+  fi
   return ${save_ret}
 }
 
@@ -287,6 +300,7 @@
 bootstat: Service started: /system/bin/bootstat --record_boot_reason
 bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
 bootstat: Service started: /system/bin/bootstat -l
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
 bootstat: Battery level at shutdown 100%
 bootstat: Battery level at startup 100%
 init    : Parsing file /system/etc/init/bootstat.rc...
@@ -407,29 +421,31 @@
        tr ' \f\t\r\n' '_____'`
   case ${var} in
     watchdog | watchdog,?* ) ;;
-    kernel_panic | kernel_panic,?*) ;;
-    recovery | recovery,?*) ;;
-    bootloader | bootloader,?*) ;;
-    cold | cold,?*) ;;
-    hard | hard,?*) ;;
-    warm | warm,?*) ;;
-    shutdown | shutdown,?*) ;;
+    kernel_panic | kernel_panic,?* ) ;;
+    recovery | recovery,?* ) ;;
+    bootloader | bootloader,?* ) ;;
+    cold | cold,?* ) ;;
+    hard | hard,?* ) ;;
+    warm | warm,?* ) ;;
+    shutdown | shutdown,?* ) ;;
     reboot,reboot | reboot,reboot,* )     var=${var#reboot,} ; var=${var%,} ;;
     reboot,cold | reboot,cold,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,hard | reboot,hard,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,warm | reboot,warm,* )         var=${var#reboot,} ; var=${var%,} ;;
     reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;;
     reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
-    reboot | reboot,?*) ;;
+    reboot | reboot,?* ) ;;
     # Aliases and Heuristics
-    *wdog* | *watchdog* )     var="watchdog" ;;
-    *powerkey* )              var="cold,powerkey" ;;
-    *panic* | *kernel_panic*) var="kernel_panic" ;;
-    *thermal*)                var="shutdown,thermal" ;;
-    *s3_wakeup*)              var="warm,s3_wakeup" ;;
-    *hw_reset*)               var="hard,hw_reset" ;;
-    *bootloader*)             var="bootloader" ;;
-    *)                        var="reboot" ;;
+    *wdog* | *watchdog* )                   var="watchdog" ;;
+    *powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
+    *panic* | *kernel_panic* )              var="kernel_panic" ;;
+    *thermal* )                             var="shutdown,thermal" ;;
+    *s3_wakeup* )                           var="warm,s3_wakeup" ;;
+    *hw_reset* )                            var="hard,hw_reset" ;;
+    *usb* )                                 var="cold,charger" ;;
+    *rtc* )                                 var="cold,rtc" ;;
+    *bootloader* )                          var="bootloader" ;;
+    * )                                     var="reboot" ;;
   esac
   echo ${var}
 }
@@ -792,6 +808,63 @@
   exitPstore
 }
 
+[ "USAGE: test_kernel_panic_subreason
+
+kernel_panic_subreason test:
+- echo SysRq : Trigger a crash : 'test' | adb shell su root tee /dev/kmsg
+- echo c | adb shell su root tee /proc/sysrq-trigger
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report kernel_panic,sysrq,test" ]
+test_kernel_panic_subreason() {
+  checkDebugBuild || return
+  duration_test ">90"
+  panic_msg="kernel_panic,sysrq,test"
+  enterPstore
+  if [ ${?} != 0 ]; then
+    echo "         or functional bootloader" >&2
+    panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
+    pstore_ok=true
+  fi
+  echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg
+  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  report_bootstat_logs kernel_panic,sysrq,test \
+    "-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
+  exitPstore
+}
+
+[ "USAGE: test_kernel_panic_hung
+
+kernel_panic_hung test:
+- echo Kernel panic - not synching: hung_task: blocked tasks |
+  adb shell su root tee /dev/kmsg
+- adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report kernel_panic,hung" ]
+test_kernel_panic_hung() {
+  checkDebugBuild || return
+  duration_test
+  panic_msg="kernel_panic,hung"
+  enterPstore
+  if [ ${?} != 0 ]; then
+    echo "         or functional bootloader" >&2
+    panic_msg="\(kernel_panic,hung\|reboot,hung\)"
+    pstore_ok=true
+  fi
+  echo "Kernel panic - not syncing: hung_task: blocked tasks" |
+    adb shell su root tee /dev/kmsg
+  adb reboot warm
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  report_bootstat_logs kernel_panic,hung
+  exitPstore
+}
+
 [ "USAGE: test_warm
 
 warm test
@@ -1051,12 +1124,12 @@
   if [ -z "${2}" ]; then
     # Hard coded should shell fail to find them above (search/permission issues)
     eval set properties ota cold factory_reset hard battery unknown \
-             kernel_panic warm thermal_shutdown userrequested_shutdown \
-             shell_reboot adb_reboot Its_Just_So_Hard_reboot \
-             bootloader_normal bootloader_watchdog bootloader_kernel_panic \
-             bootloader_oem_powerkey bootloader_wdog_reset \
-             bootloader_wdog_reset bootloader_wdog_reset bootloader_hard \
-             bootloader_recovery
+             kernel_panic kernel_panic_subreason kernel_panic_hung warm \
+             thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \
+             Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
+             bootloader_kernel_panic bootloader_oem_powerkey \
+             bootloader_wdog_reset bootloader_wdog_reset bootloader_wdog_reset \
+             bootloader_hard bootloader_recovery
   fi
   if [ X"nothing" = X"${1}" ]; then
     shift 1
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a1fe6ed..e60e6be 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -29,7 +29,9 @@
 #include <ctime>
 #include <map>
 #include <memory>
+#include <regex>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -138,7 +140,7 @@
 // values.
 const std::map<std::string, int32_t> kBootReasonMap = {
     {"empty", kEmptyBootReason},
-    {"unknown", kUnknownBootReason},
+    {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
     {"normal", 2},
     {"recovery", 3},
     {"reboot", 4},
@@ -191,12 +193,14 @@
     {"s3_wakeup", 51},
     {"kernel_panic,sysrq", 52},
     {"kernel_panic,NULL", 53},
+    {"kernel_panic,null", 53},
     {"kernel_panic,BUG", 54},
+    {"kernel_panic,bug", 54},
     {"bootloader", 55},
     {"cold", 56},
     {"hard", 57},
     {"warm", 58},
-    {"recovery", 59},
+    // {"recovery", 59},  // Duplicate of enum 3 above. Immediate reuse possible.
     {"thermal-shutdown", 60},
     {"shutdown,thermal", 61},
     {"shutdown,battery", 62},
@@ -227,7 +231,7 @@
     {"shutdown,thermal,battery", 87},
     {"reboot,its_just_so_hard", 88},  // produced by boot_reason_test
     {"reboot,Its Just So Hard", 89},  // produced by boot_reason_test
-    {"usb", 90},
+    // {"usb", 90},  // Duplicate of enum 80 above. Immediate reuse possible.
     {"charge", 91},
     {"oem_tz_crash", 92},
     {"uvlo", 93},
@@ -285,6 +289,19 @@
     {"oem_sdi_err_fatal", 145},
     {"pmic_watchdog", 146},
     {"software_master", 147},
+    {"cold,charger", 148},
+    {"cold,rtc", 149},
+    {"cold,rtc,2sec", 150},
+    {"reboot,tool", 151},
+    {"reboot,wdt", 152},
+    {"reboot,unknown", 153},
+    {"kernel_panic,audit", 154},
+    {"kernel_panic,atomic", 155},
+    {"kernel_panic,hung", 156},
+    {"kernel_panic,hung,rcu", 157},
+    {"kernel_panic,init", 158},
+    {"kernel_panic,oom", 159},
+    {"kernel_panic,stack", 160},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -461,11 +478,13 @@
     }
     return std::string::npos;
   }
+
+  operator const std::string&() const { return console; }
 };
 
 // If bit error match to needle, correct it.
 // Return true if any corrections were discovered and applied.
-bool correctForBer(std::string& reason, const std::string& needle) {
+bool correctForBitError(std::string& reason, const std::string& needle) {
   bool corrected = false;
   if (reason.length() < needle.length()) return corrected;
   const pstoreConsole console(reason);
@@ -483,20 +502,215 @@
   return corrected;
 }
 
+// If bit error match to needle, correct it.
+// Return true if any corrections were discovered and applied.
+// Try again if we can replace underline with spaces.
+bool correctForBitErrorOrUnderline(std::string& reason, const std::string& needle) {
+  bool corrected = correctForBitError(reason, needle);
+  std::string _needle(needle);
+  std::transform(_needle.begin(), _needle.end(), _needle.begin(),
+                 [](char c) { return (c == '_') ? ' ' : c; });
+  if (needle != _needle) {
+    corrected |= correctForBitError(reason, _needle);
+  }
+  return corrected;
+}
+
+// Converts a string value representing the reason the system booted to a
+// string complying with Android system standard reason.
+void transformReason(std::string& reason) {
+  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+  std::transform(reason.begin(), reason.end(), reason.begin(),
+                 [](char c) { return ::isblank(c) ? '_' : c; });
+  std::transform(reason.begin(), reason.end(), reason.begin(),
+                 [](char c) { return ::isprint(c) ? c : '?'; });
+}
+
+// Check subreasons for reboot,<subreason> kernel_panic,sysrq,<subreason> or
+// kernel_panic,<subreason>.
+//
+// If quoted flag is set, pull out and correct single quoted ('), newline (\n)
+// or unprintable character terminated subreason, pos is supplied just beyond
+// first quote.  if quoted false, pull out and correct newline (\n) or
+// unprintable character terminated subreason.
+//
+// Heuristics to find termination is painted into a corner:
+
+// single bit error for quote ' that we can block.  It is acceptable for
+// the others 7, g in reason.  2/9 chance will miss the terminating quote,
+// but there is always the terminating newline that usually immediately
+// follows to fortify our chances.
+bool likely_single_quote(char c) {
+  switch (static_cast<uint8_t>(c)) {
+    case '\'':         // '\''
+    case '\'' ^ 0x01:  // '&'
+    case '\'' ^ 0x02:  // '%'
+    case '\'' ^ 0x04:  // '#'
+    case '\'' ^ 0x08:  // '/'
+      return true;
+    case '\'' ^ 0x10:  // '7'
+      break;
+    case '\'' ^ 0x20:  // '\a' (unprintable)
+      return true;
+    case '\'' ^ 0x40:  // 'g'
+      break;
+    case '\'' ^ 0x80:  // 0xA7 (unprintable)
+      return true;
+  }
+  return false;
+}
+
+// ::isprint(c) and likely_space() will prevent us from being called for
+// fundamentally printable entries, except for '\r' and '\b'.
+//
+// Except for * and J, single bit errors for \n, all others are non-
+// printable so easy catch.  It is _acceptable_ for *, J or j to exist in
+// the reason string, so 2/9 chance we will miss the terminating newline.
+//
+// NB: J might not be acceptable, except if at the beginning or preceded
+//     with a space, '(' or any of the quotes and their BER aliases.
+// NB: * might not be acceptable, except if at the beginning or preceded
+//     with a space, another *, or any of the quotes or their BER aliases.
+//
+// To reduce the chances to closer to 1/9 is too complicated for the gain.
+bool likely_newline(char c) {
+  switch (static_cast<uint8_t>(c)) {
+    case '\n':         // '\n' (unprintable)
+    case '\n' ^ 0x01:  // '\r' (unprintable)
+    case '\n' ^ 0x02:  // '\b' (unprintable)
+    case '\n' ^ 0x04:  // 0x0E (unprintable)
+    case '\n' ^ 0x08:  // 0x02 (unprintable)
+    case '\n' ^ 0x10:  // 0x1A (unprintable)
+      return true;
+    case '\n' ^ 0x20:  // '*'
+    case '\n' ^ 0x40:  // 'J'
+      break;
+    case '\n' ^ 0x80:  // 0x8A (unprintable)
+      return true;
+  }
+  return false;
+}
+
+// ::isprint(c) will prevent us from being called for all the printable
+// matches below.  If we let unprintables through because of this, they
+// get converted to underscore (_) by the validation phase.
+bool likely_space(char c) {
+  switch (static_cast<uint8_t>(c)) {
+    case ' ':          // ' '
+    case ' ' ^ 0x01:   // '!'
+    case ' ' ^ 0x02:   // '"'
+    case ' ' ^ 0x04:   // '$'
+    case ' ' ^ 0x08:   // '('
+    case ' ' ^ 0x10:   // '0'
+    case ' ' ^ 0x20:   // '\0' (unprintable)
+    case ' ' ^ 0x40:   // 'P'
+    case ' ' ^ 0x80:   // 0xA0 (unprintable)
+    case '\t':         // '\t'
+    case '\t' ^ 0x01:  // '\b' (unprintable) (likely_newline counters)
+    case '\t' ^ 0x02:  // '\v' (unprintable)
+    case '\t' ^ 0x04:  // '\r' (unprintable) (likely_newline counters)
+    case '\t' ^ 0x08:  // 0x01 (unprintable)
+    case '\t' ^ 0x10:  // 0x19 (unprintable)
+    case '\t' ^ 0x20:  // ')'
+    case '\t' ^ 0x40:  // '1'
+    case '\t' ^ 0x80:  // 0x89 (unprintable)
+      return true;
+  }
+  return false;
+}
+
+std::string getSubreason(const std::string& content, size_t pos, bool quoted) {
+  static constexpr size_t max_reason_length = 256;
+
+  std::string subReason(content.substr(pos, max_reason_length));
+  // Correct against any known strings that Bit Error Match
+  for (const auto& s : knownReasons) {
+    correctForBitErrorOrUnderline(subReason, s);
+  }
+  std::string terminator(quoted ? "'" : "");
+  for (const auto& m : kBootReasonMap) {
+    if (m.first.length() <= strlen("cold")) continue;  // too short?
+    if (correctForBitErrorOrUnderline(subReason, m.first + terminator)) continue;
+    if (m.first.length() <= strlen("reboot,cold")) continue;  // short?
+    if (android::base::StartsWith(m.first, "reboot,")) {
+      correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + terminator);
+    } else if (android::base::StartsWith(m.first, "kernel_panic,sysrq,")) {
+      correctForBitErrorOrUnderline(subReason,
+                                    m.first.substr(strlen("kernel_panic,sysrq,")) + terminator);
+    } else if (android::base::StartsWith(m.first, "kernel_panic,")) {
+      correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("kernel_panic,")) + terminator);
+    }
+  }
+  for (pos = 0; pos < subReason.length(); ++pos) {
+    char c = subReason[pos];
+    if (!(::isprint(c) || likely_space(c)) || likely_newline(c) ||
+        (quoted && likely_single_quote(c))) {
+      subReason.erase(pos);
+      break;
+    }
+  }
+  transformReason(subReason);
+  return subReason;
+}
+
 bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {
   // Check for kernel panic types to refine information
-  if (console.rfind("SysRq : Trigger a crash") != std::string::npos) {
-    // Can not happen, except on userdebug, during testing/debugging.
+  if ((console.rfind("SysRq : Trigger a crash") != std::string::npos) ||
+      (console.rfind("PC is at sysrq_handle_crash+") != std::string::npos)) {
     ret = "kernel_panic,sysrq";
+    // Invented for Android to allow daemons that specifically trigger sysrq
+    // to communicate more accurate boot subreasons via last console messages.
+    static constexpr char sysrqSubreason[] = "SysRq : Trigger a crash : '";
+    auto pos = console.rfind(sysrqSubreason);
+    if (pos != std::string::npos) {
+      ret += "," + getSubreason(console, pos + strlen(sysrqSubreason), /* quoted */ true);
+    }
     return true;
   }
   if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") !=
       std::string::npos) {
-    ret = "kernel_panic,NULL";
+    ret = "kernel_panic,null";
     return true;
   }
   if (console.rfind("Kernel BUG at ") != std::string::npos) {
-    ret = "kernel_panic,BUG";
+    ret = "kernel_panic,bug";
+    return true;
+  }
+
+  std::string panic("Kernel panic - not syncing: ");
+  auto pos = console.rfind(panic);
+  if (pos != std::string::npos) {
+    static const std::vector<std::pair<const std::string, const std::string>> panicReasons = {
+        {"Out of memory", "oom"},
+        {"out of memory", "oom"},
+        {"Oh boy, that early out of memory", "oom"},  // omg
+        {"BUG!", "bug"},
+        {"hung_task: blocked tasks", "hung"},
+        {"audit: ", "audit"},
+        {"scheduling while atomic", "atomic"},
+        {"Attempted to kill init!", "init"},
+        {"Requested init", "init"},
+        {"No working init", "init"},
+        {"Could not decompress init", "init"},
+        {"RCU Stall", "hung,rcu"},
+        {"stack-protector", "stack"},
+        {"kernel stack overflow", "stack"},
+        {"Corrupt kernel stack", "stack"},
+        {"low stack detected", "stack"},
+        {"corrupted stack end", "stack"},
+    };
+
+    ret = "kernel_panic";
+    for (auto& s : panicReasons) {
+      if (console.find(panic + s.first, pos) != std::string::npos) {
+        ret += "," + s.second;
+        return true;
+      }
+    }
+    auto reason = getSubreason(console, pos + panic.length(), /* newline */ false);
+    if (reason.length() > 3) {
+      ret += "," + reason;
+    }
     return true;
   }
   return false;
@@ -506,32 +720,12 @@
   return addKernelPanicSubReason(pstoreConsole(content), ret);
 }
 
-// std::transform Helper callback functions:
-// Converts a string value representing the reason the system booted to a
-// string complying with Android system standard reason.
-char tounderline(char c) {
-  return ::isblank(c) ? '_' : c;
-}
-
-char toprintable(char c) {
-  return ::isprint(c) ? c : '?';
-}
-
-// Cleanup boot_reason regarding acceptable character set
-void transformReason(std::string& reason) {
-  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
-  std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
-  std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
-}
-
 const char system_reboot_reason_property[] = "sys.boot.reason";
 const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
 const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
 
 // Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
 std::string BootReasonStrToReason(const std::string& boot_reason) {
-  static const size_t max_reason_length = 256;
-
   std::string ret(GetProperty(system_reboot_reason_property));
   std::string reason(boot_reason);
   // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
@@ -566,26 +760,36 @@
     // A series of checks to take some officially unsupported reasons
     // reported by the bootloader and find some logical and canonical
     // sense.  In an ideal world, we would require those bootloaders
-    // to behave and follow our standards.
+    // to behave and follow our CTS standards.
+    //
+    // first member is the output
+    // second member is an unanchored regex for an alias
+    //
+    // If output has a prefix of <bang> '!', we do not use it as a
+    // match needle (and drop the <bang> prefix when landing in output),
+    // otherwise look for it as well. This helps keep the scale of the
+    // following table smaller.
     static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
         {"watchdog", "wdog"},
-        {"cold,powerkey", "powerkey"},
+        {"cold,powerkey", "powerkey|power_key|PowerKey"},
         {"kernel_panic", "panic"},
         {"shutdown,thermal", "thermal"},
         {"warm,s3_wakeup", "s3_wakeup"},
         {"hard,hw_reset", "hw_reset"},
+        {"cold,charger", "usb"},
+        {"cold,rtc", "rtc"},
         {"reboot,2sec", "2sec_reboot"},
         {"bootloader", ""},
     };
 
-    // Either the primary or alias is found _somewhere_ in the reason string.
     for (auto& s : aliasReasons) {
-      if (reason.find(s.first) != std::string::npos) {
+      size_t firstHasNot = s.first[0] == '!';
+      if (!firstHasNot && (reason.find(s.first) != std::string::npos)) {
         ret = s.first;
         break;
       }
-      if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
-        ret = s.first;
+      if (s.second.size() && std::regex_search(reason, std::regex(s.second))) {
+        ret = s.first.substr(firstHasNot);
         break;
       }
     }
@@ -624,28 +828,7 @@
       static const char cmd[] = "reboot: Restarting system with command '";
       size_t pos = console.rfind(cmd);
       if (pos != std::string::npos) {
-        pos += strlen(cmd);
-        std::string subReason(content.substr(pos, max_reason_length));
-        // Correct against any known strings that Bit Error Match
-        for (const auto& s : knownReasons) {
-          correctForBer(subReason, s);
-        }
-        for (const auto& m : kBootReasonMap) {
-          if (m.first.length() <= strlen("cold")) continue;  // too short?
-          if (correctForBer(subReason, m.first + "'")) continue;
-          if (m.first.length() <= strlen("reboot,cold")) continue;  // short?
-          if (!android::base::StartsWith(m.first, "reboot,")) continue;
-          correctForBer(subReason, m.first.substr(strlen("reboot,")) + "'");
-        }
-        for (pos = 0; pos < subReason.length(); ++pos) {
-          char c = subReason[pos];
-          // #, &, %, / are common single bit error for ' that we can block
-          if (!::isprint(c) || (c == '\'') || (c == '#') || (c == '&') || (c == '%') || (c == '/')) {
-            subReason.erase(pos);
-            break;
-          }
-        }
-        transformReason(subReason);
+        std::string subReason(getSubreason(content, pos + strlen(cmd), /* quoted */ true));
         if (subReason != "") {  // Will not land "reboot" as that is too blunt.
           if (isKernelRebootReason(subReason)) {
             ret = "reboot," + subReason;  // User space can't talk kernel reasons.
@@ -683,7 +866,7 @@
       if (pos != std::string::npos) {
         digits = content.substr(pos + strlen(battery), strlen("100 "));
         // correct common errors
-        correctForBer(digits, "100 ");
+        correctForBitError(digits, "100 ");
         if (digits[0] == '!') digits[0] = '1';
         if (digits[1] == '!') digits[1] = '1';
       }
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 3bc1742..c03b41d 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -20,4 +20,5 @@
 LOCAL_SRC_FILES_arm64 := seccomp_policy/crash_dump.arm64.policy
 LOCAL_SRC_FILES_x86 := seccomp_policy/crash_dump.x86.policy
 LOCAL_SRC_FILES_x86_64 := seccomp_policy/crash_dump.x86_64.policy
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
 include $(BUILD_PREBUILT)
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f8b4bad..397ff2f 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -20,6 +20,7 @@
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
+#include <sys/resource.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -570,7 +571,7 @@
 static const char* const kDebuggerdSeccompPolicy =
     "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
 
-pid_t seccomp_fork() {
+static pid_t seccomp_fork_impl(void (*prejail)()) {
   unique_fd policy_fd(open(kDebuggerdSeccompPolicy, O_RDONLY | O_CLOEXEC));
   if (policy_fd == -1) {
     LOG(FATAL) << "failed to open policy " << kDebuggerdSeccompPolicy;
@@ -607,10 +608,18 @@
     continue;
   }
 
+  if (prejail) {
+    prejail();
+  }
+
   minijail_enter(jail.get());
   return result;
 }
 
+static pid_t seccomp_fork() {
+  return seccomp_fork_impl(nullptr);
+}
+
 TEST_F(CrasherTest, seccomp_crash) {
   int intercept_result;
   unique_fd output_fd;
@@ -628,6 +637,46 @@
   ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
+static pid_t seccomp_fork_rlimit() {
+  return seccomp_fork_impl([]() {
+    struct rlimit rlim = {
+        .rlim_cur = 512 * 1024 * 1024,
+        .rlim_max = 512 * 1024 * 1024,
+    };
+
+    if (setrlimit(RLIMIT_AS, &rlim) != 0) {
+      raise(SIGINT);
+    }
+  });
+}
+
+TEST_F(CrasherTest, seccomp_crash_oom) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  StartProcess(
+      []() {
+        std::vector<void*> vec;
+        for (int i = 0; i < 512; ++i) {
+          char* buf = static_cast<char*>(malloc(1024 * 1024));
+          if (!buf) {
+            abort();
+          }
+          memset(buf, 0xff, 1024 * 1024);
+          vec.push_back(buf);
+        }
+      },
+      &seccomp_fork_rlimit);
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  // We can't actually generate a backtrace, just make sure that the process terminates.
+}
+
 __attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) {
   siginfo_t siginfo;
   siginfo.si_code = SI_QUEUE;
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 364fca5..dea2e17 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -37,6 +37,7 @@
 
 #include <atomic>
 #include <memory>
+#include <mutex>
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
@@ -298,11 +299,13 @@
 static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
   // Only allow one thread to handle a crash at a time (this can happen multiple times without
   // exit, since tombstones can be requested without a real crash happening.)
-  static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
-  int ret = pthread_mutex_lock(&crash_mutex);
-  if (ret != 0) {
-    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
-    return;
+  static std::recursive_mutex crash_mutex;
+  static int lock_count;
+
+  crash_mutex.lock();
+  if (lock_count++ > 0) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+    _exit(1);
   }
 
   unique_fd tombstone_socket, output_fd;
@@ -313,7 +316,8 @@
     tombstoned_notify_completion(tombstone_socket.get());
   }
 
-  pthread_mutex_unlock(&crash_mutex);
+  --lock_count;
+  crash_mutex.unlock();
 }
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
new file mode 100644
index 0000000..a7ecf37
--- /dev/null
+++ b/diagnose_usb/Android.bp
@@ -0,0 +1,13 @@
+cc_library_static {
+    name: "libdiagnose_usb",
+    cflags: ["-Wall", "-Wextra", "-Werror"],
+    host_supported: true,
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
+    srcs: ["diagnose_usb.cpp"],
+    export_include_dirs: ["include"],
+    static_libs: ["libbase"],
+}
diff --git a/diagnose_usb/OWNERS b/diagnose_usb/OWNERS
new file mode 100644
index 0000000..643b448
--- /dev/null
+++ b/diagnose_usb/OWNERS
@@ -0,0 +1,2 @@
+jmgao@google.com
+yabinc@google.com
diff --git a/adb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
similarity index 97%
rename from adb/diagnose_usb.cpp
rename to diagnose_usb/diagnose_usb.cpp
index 9f721bf..5695ece 100644
--- a/adb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -33,7 +33,7 @@
 // Returns a message describing any potential problems we find with udev, or an empty string if we
 // can't find plugdev information (i.e. udev is not installed).
 static std::string GetUdevProblem() {
-#if defined(__linux__)
+#if defined(__linux__) && !defined(__BIONIC__)
     errno = 0;
     group* plugdev_group = getgrnam("plugdev");
 
diff --git a/adb/diagnose_usb.h b/diagnose_usb/include/diagnose_usb.h
similarity index 100%
rename from adb/diagnose_usb.h
rename to diagnose_usb/include/diagnose_usb.h
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index dfcf090..f5bcc26 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -39,13 +39,15 @@
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid mke2fs.conf make_f2fs sload_f2fs
+LOCAL_REQUIRED_MODULES := mke2fs make_f2fs
 
 LOCAL_SRC_FILES_linux := usb_linux.cpp
 LOCAL_STATIC_LIBRARIES_linux := libselinux
+LOCAL_REQUIRED_MODULES_linux := e2fsdroid mke2fs.conf sload_f2fs
 
 LOCAL_SRC_FILES_darwin := usb_osx.cpp
 LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_REQUIRED_MODULES_darwin := e2fsdroid mke2fs.conf sload_f2fs
 LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
 LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 271b792..60b7124 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -27,7 +27,6 @@
  */
 
 #include "fastboot.h"
-#include "fs.h"
 
 #include <errno.h>
 #include <stdarg.h>
@@ -38,150 +37,117 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#define OP_DOWNLOAD   1
-#define OP_COMMAND    2
-#define OP_QUERY      3
-#define OP_NOTICE     4
-#define OP_DOWNLOAD_SPARSE 5
-#define OP_WAIT_FOR_DISCONNECT 6
-#define OP_DOWNLOAD_FD 7
-#define OP_UPLOAD 8
+#include <memory>
+#include <vector>
 
-typedef struct Action Action;
+#include <android-base/stringprintf.h>
 
-#define CMD_SIZE 64
-
-struct Action {
-    unsigned op;
-    Action* next;
-
-    char cmd[CMD_SIZE];
-    const char* prod;
-    void* data;
-    int fd;
-
-    // The protocol only supports 32-bit sizes, so you'll have to break
-    // anything larger into chunks.
-    uint32_t size;
-
-    const char *msg;
-    int (*func)(Action* a, int status, const char* resp);
-
-    double start;
+enum Op {
+    OP_DOWNLOAD,
+    OP_COMMAND,
+    OP_QUERY,
+    OP_NOTICE,
+    OP_DOWNLOAD_SPARSE,
+    OP_WAIT_FOR_DISCONNECT,
+    OP_DOWNLOAD_FD,
+    OP_UPLOAD,
 };
 
-static Action *action_list = 0;
-static Action *action_last = 0;
+struct Action {
+    Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {}
 
+    Op op;
+    std::string cmd;
+    std::string msg;
 
+    std::string product;
 
+    void* data = nullptr;
+    // The protocol only supports 32-bit sizes, so you'll have to break
+    // anything larger into multiple chunks.
+    uint32_t size = 0;
+
+    int fd = -1;
+
+    int (*func)(Action& a, int status, const char* resp) = nullptr;
+
+    double start = -1;
+};
+
+static std::vector<std::unique_ptr<Action>> action_list;
 
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
-    std::string cmd = "getvar:";
-    cmd += key;
+    std::string cmd = "getvar:" + key;
 
     char buf[FB_RESPONSE_SZ + 1];
     memset(buf, 0, sizeof(buf));
-    if (fb_command_response(transport, cmd.c_str(), buf)) {
-      return false;
+    if (fb_command_response(transport, cmd, buf)) {
+        return false;
     }
     *value = buf;
     return true;
 }
 
-static int cb_default(Action* a, int status, const char* resp) {
+static int cb_default(Action& a, int status, const char* resp) {
     if (status) {
         fprintf(stderr,"FAILED (%s)\n", resp);
     } else {
         double split = now();
-        fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
-        a->start = split;
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
+        a.start = split;
     }
     return status;
 }
 
-static Action *queue_action(unsigned op, const char *fmt, ...)
-{
-    va_list ap;
-    size_t cmdsize;
-
-    Action* a = reinterpret_cast<Action*>(calloc(1, sizeof(Action)));
-    if (a == nullptr) die("out of memory");
-
-    va_start(ap, fmt);
-    cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
-    va_end(ap);
-
-    if (cmdsize >= sizeof(a->cmd)) {
-        free(a);
-        die("Command length (%zu) exceeds maximum size (%zu)", cmdsize, sizeof(a->cmd));
-    }
-
-    if (action_last) {
-        action_last->next = a;
-    } else {
-        action_list = a;
-    }
-    action_last = a;
-    a->op = op;
+static Action& queue_action(Op op, const std::string& cmd) {
+    std::unique_ptr<Action> a{new Action(op, cmd)};
     a->func = cb_default;
 
-    a->start = -1;
-
-    return a;
+    action_list.push_back(std::move(a));
+    return *action_list.back();
 }
 
-void fb_set_active(const char *slot)
-{
-    Action *a;
-    a = queue_action(OP_COMMAND, "set_active:%s", slot);
-    a->msg = mkmsg("Setting current slot to '%s'", slot);
+void fb_set_active(const std::string& slot) {
+    Action& a = queue_action(OP_COMMAND, "set_active:" + slot);
+    a.msg = "Setting current slot to '" + slot + "'...";
 }
 
-void fb_queue_erase(const char *ptn)
-{
-    Action *a;
-    a = queue_action(OP_COMMAND, "erase:%s", ptn);
-    a->msg = mkmsg("erasing '%s'", ptn);
+void fb_queue_erase(const std::string& partition) {
+    Action& a = queue_action(OP_COMMAND, "erase:" + partition);
+    a.msg = "Erasing '" + partition + "'...";
 }
 
-void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz)
-{
-    Action *a;
+void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) {
+    Action& a = queue_action(OP_DOWNLOAD_FD, "");
+    a.fd = fd;
+    a.size = sz;
+    a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024);
 
-    a = queue_action(OP_DOWNLOAD_FD, "");
-    a->fd = fd;
-    a->size = sz;
-    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
-
-    a = queue_action(OP_COMMAND, "flash:%s", ptn);
-    a->msg = mkmsg("writing '%s'", ptn);
+    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    b.msg = "Writing '" + partition + "'...";
 }
 
-void fb_queue_flash(const char *ptn, void *data, uint32_t sz)
-{
-    Action *a;
+void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) {
+    Action& a = queue_action(OP_DOWNLOAD, "");
+    a.data = data;
+    a.size = sz;
+    a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024);
 
-    a = queue_action(OP_DOWNLOAD, "");
-    a->data = data;
-    a->size = sz;
-    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
-
-    a = queue_action(OP_COMMAND, "flash:%s", ptn);
-    a->msg = mkmsg("writing '%s'", ptn);
+    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    b.msg = "Writing '" + partition + "'...";
 }
 
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
-                           size_t total) {
-    Action *a;
+void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
+                           size_t current, size_t total) {
+    Action& a = queue_action(OP_DOWNLOAD_SPARSE, "");
+    a.data = s;
+    a.size = 0;
+    a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%d KB)...", partition.c_str(),
+                                        current, total, sz / 1024);
 
-    a = queue_action(OP_DOWNLOAD_SPARSE, "");
-    a->data = s;
-    a->size = 0;
-    a->msg = mkmsg("sending sparse '%s' %zu/%zu (%d KB)", ptn, current, total, sz / 1024);
-
-    a = queue_action(OP_COMMAND, "flash:%s", ptn);
-    a->msg = mkmsg("writing '%s' %zu/%zu", ptn, current, total);
+    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    b.msg =
+        android::base::StringPrintf("Writing '%s' %zu/%zu...", partition.c_str(), current, total);
 }
 
 static int match(const char* str, const char** value, unsigned count) {
@@ -205,212 +171,181 @@
     return 0;
 }
 
-
-
-static int cb_check(Action* a, int status, const char* resp, int invert)
-{
-    const char** value = reinterpret_cast<const char**>(a->data);
-    unsigned count = a->size;
+static int cb_check(Action& a, int status, const char* resp, int invert) {
+    const char** value = reinterpret_cast<const char**>(a.data);
+    unsigned count = a.size;
     unsigned n;
-    int yes;
 
     if (status) {
         fprintf(stderr,"FAILED (%s)\n", resp);
         return status;
     }
 
-    if (a->prod) {
-        if (strcmp(a->prod, cur_product) != 0) {
+    if (!a.product.empty()) {
+        if (a.product != cur_product) {
             double split = now();
-            fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n",
-                    cur_product, a->prod, (split - a->start));
-            a->start = split;
+            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
+                    a.product.c_str(), (split - a.start));
+            a.start = split;
             return 0;
         }
     }
 
-    yes = match(resp, value, count);
+    int yes = match(resp, value, count);
     if (invert) yes = !yes;
 
     if (yes) {
         double split = now();
-        fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
-        a->start = split;
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
+        a.start = split;
         return 0;
     }
 
-    fprintf(stderr,"FAILED\n\n");
-    fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
-    fprintf(stderr,"Update %s '%s'",
-            invert ? "rejects" : "requires", value[0]);
+    fprintf(stderr, "FAILED\n\n");
+    fprintf(stderr, "Device %s is '%s'.\n", a.cmd.c_str() + 7, resp);
+    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", value[0]);
     for (n = 1; n < count; n++) {
-        fprintf(stderr," or '%s'", value[n]);
+        fprintf(stderr, " or '%s'", value[n]);
     }
-    fprintf(stderr,".\n\n");
+    fprintf(stderr, ".\n\n");
     return -1;
 }
 
-static int cb_require(Action*a, int status, const char* resp) {
+static int cb_require(Action& a, int status, const char* resp) {
     return cb_check(a, status, resp, 0);
 }
 
-static int cb_reject(Action* a, int status, const char* resp) {
+static int cb_reject(Action& a, int status, const char* resp) {
     return cb_check(a, status, resp, 1);
 }
 
-static char* xstrdup(const char* s) {
-    char* result = strdup(s);
-    if (!result) die("out of memory");
-    return result;
+void fb_queue_require(const std::string& product, const std::string& var, bool invert,
+                      size_t nvalues, const char** values) {
+    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    a.product = product;
+    a.data = values;
+    a.size = nvalues;
+    a.msg = "Checking " + var;
+    a.func = invert ? cb_reject : cb_require;
+    if (a.data == nullptr) die("out of memory");
 }
 
-void fb_queue_require(const char *prod, const char *var,
-                      bool invert, size_t nvalues, const char **value)
-{
-    Action *a;
-    a = queue_action(OP_QUERY, "getvar:%s", var);
-    a->prod = prod;
-    a->data = value;
-    a->size = nvalues;
-    a->msg = mkmsg("checking %s", var);
-    a->func = invert ? cb_reject : cb_require;
-    if (a->data == nullptr) die("out of memory");
-}
-
-static int cb_display(Action* a, int status, const char* resp) {
+static int cb_display(Action& a, int status, const char* resp) {
     if (status) {
-        fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
+        fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
         return status;
     }
-    fprintf(stderr, "%s: %s\n", static_cast<const char*>(a->data), resp);
-    free(static_cast<char*>(a->data));
+    fprintf(stderr, "%s: %s\n", static_cast<const char*>(a.data), resp);
+    free(static_cast<char*>(a.data));
     return 0;
 }
 
-void fb_queue_display(const char* var, const char* prettyname) {
-    Action* a = queue_action(OP_QUERY, "getvar:%s", var);
-    a->data = xstrdup(prettyname);
-    a->func = cb_display;
+void fb_queue_display(const std::string& label, const std::string& var) {
+    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    a.data = xstrdup(label.c_str());
+    a.func = cb_display;
 }
 
-static int cb_save(Action* a, int status, const char* resp) {
+static int cb_save(Action& a, int status, const char* resp) {
     if (status) {
-        fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
+        fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
         return status;
     }
-    strncpy(reinterpret_cast<char*>(a->data), resp, a->size);
+    strncpy(reinterpret_cast<char*>(a.data), resp, a.size);
     return 0;
 }
 
-void fb_queue_query_save(const char* var, char* dest, uint32_t dest_size) {
-    Action* a = queue_action(OP_QUERY, "getvar:%s", var);
-    a->data = dest;
-    a->size = dest_size;
-    a->func = cb_save;
+void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
+    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    a.data = dest;
+    a.size = dest_size;
+    a.func = cb_save;
 }
 
-static int cb_do_nothing(Action*, int , const char*) {
-    fprintf(stderr,"\n");
+static int cb_do_nothing(Action&, int, const char*) {
+    fprintf(stderr, "\n");
     return 0;
 }
 
-void fb_queue_reboot(void)
-{
-    Action *a = queue_action(OP_COMMAND, "reboot");
-    a->func = cb_do_nothing;
-    a->msg = "rebooting";
+void fb_queue_reboot() {
+    Action& a = queue_action(OP_COMMAND, "reboot");
+    a.func = cb_do_nothing;
+    a.msg = "Rebooting...";
 }
 
-void fb_queue_command(const char *cmd, const char *msg)
-{
-    Action *a = queue_action(OP_COMMAND, cmd);
-    a->msg = msg;
+void fb_queue_command(const std::string& cmd, const std::string& msg) {
+    Action& a = queue_action(OP_COMMAND, cmd);
+    a.msg = msg;
 }
 
-void fb_queue_download(const char *name, void *data, uint32_t size)
-{
-    Action *a = queue_action(OP_DOWNLOAD, "");
-    a->data = data;
-    a->size = size;
-    a->msg = mkmsg("downloading '%s'", name);
+void fb_queue_download(const std::string& name, void* data, uint32_t size) {
+    Action& a = queue_action(OP_DOWNLOAD, "");
+    a.data = data;
+    a.size = size;
+    a.msg = "Downloading '" + name + "'";
 }
 
-void fb_queue_download_fd(const char *name, int fd, uint32_t sz)
-{
-    Action *a;
-    a = queue_action(OP_DOWNLOAD_FD, "");
-    a->fd = fd;
-    a->size = sz;
-    a->msg = mkmsg("sending '%s' (%d KB)", name, sz / 1024);
+void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz) {
+    Action& a = queue_action(OP_DOWNLOAD_FD, "");
+    a.fd = fd;
+    a.size = sz;
+    a.msg = android::base::StringPrintf("Sending '%s' (%d KB)", name.c_str(), sz / 1024);
 }
 
-void fb_queue_upload(const char* outfile) {
-    Action* a = queue_action(OP_UPLOAD, "");
-    a->data = xstrdup(outfile);
-    a->msg = mkmsg("uploading '%s'", outfile);
+void fb_queue_upload(const std::string& outfile) {
+    Action& a = queue_action(OP_UPLOAD, "");
+    a.data = xstrdup(outfile.c_str());
+    a.msg = "Uploading '" + outfile + "'";
 }
 
-void fb_queue_notice(const char* notice) {
-    Action *a = queue_action(OP_NOTICE, "");
-    a->data = (void*) notice;
+void fb_queue_notice(const std::string& notice) {
+    Action& a = queue_action(OP_NOTICE, "");
+    a.msg = notice;
 }
 
-void fb_queue_wait_for_disconnect(void)
-{
+void fb_queue_wait_for_disconnect() {
     queue_action(OP_WAIT_FOR_DISCONNECT, "");
 }
 
-int64_t fb_execute_queue(Transport* transport)
-{
-    Action *a;
-    char resp[FB_RESPONSE_SZ+1];
+int64_t fb_execute_queue(Transport* transport) {
     int64_t status = 0;
-
-    a = action_list;
-    if (!a)
-        return status;
-    resp[FB_RESPONSE_SZ] = 0;
-
-    double start = -1;
-    for (a = action_list; a; a = a->next) {
+    for (auto& a : action_list) {
         a->start = now();
-        if (start < 0) start = a->start;
-        if (a->msg) {
-            // fprintf(stderr,"%30s... ",a->msg);
-            fprintf(stderr,"%s...\n",a->msg);
+        if (!a->msg.empty()) {
+            fprintf(stderr, "%s\n", a->msg.c_str());
         }
         if (a->op == OP_DOWNLOAD) {
             status = fb_download_data(transport, a->data, a->size);
-            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_DOWNLOAD_FD) {
             status = fb_download_data_fd(transport, a->fd, a->size);
-            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_COMMAND) {
             status = fb_command(transport, a->cmd);
-            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_QUERY) {
+            char resp[FB_RESPONSE_SZ + 1] = {};
             status = fb_command_response(transport, a->cmd, resp);
-            status = a->func(a, status, status ? fb_get_error().c_str() : resp);
+            status = a->func(*a, status, status ? fb_get_error().c_str() : resp);
             if (status) break;
         } else if (a->op == OP_NOTICE) {
-            fprintf(stderr,"%s\n",(char*)a->data);
+            // We already showed the notice because it's in `Action::msg`.
         } else if (a->op == OP_DOWNLOAD_SPARSE) {
             status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
-            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
             transport->WaitForDisconnect();
         } else if (a->op == OP_UPLOAD) {
             status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
-            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
         } else {
-            die("bogus action");
+            die("unknown action: %d", a->op);
         }
     }
-
-    fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
+    action_list.clear();
     return status;
 }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 536d64e..237f081 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -123,6 +123,8 @@
     { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  true  },
     { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  false },
     { "dts",      "dt.img",           "dt.sig",       "dts",      true,  false },
+    { "odm",      "odm.img",          "odm.sig",      "odm",      true,  false },
+    { "product",  "product.img",      "product.sig",  "product",  true,  false },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
     { "system",   "system.img",       "system.sig",   "system",   false, false },
     { nullptr,    "system_other.img", "system.sig",   "system",   true,  true  },
@@ -633,27 +635,31 @@
     return fd.release();
 }
 
-static char *strip(char *s)
-{
-    int n;
-    while(*s && isspace(*s)) s++;
-    n = strlen(s);
-    while(n-- > 0) {
-        if(!isspace(s[n])) break;
+static char* strip(char* s) {
+    while (*s && isspace(*s)) s++;
+
+    int n = strlen(s);
+    while (n-- > 0) {
+        if (!isspace(s[n])) break;
         s[n] = 0;
     }
     return s;
 }
 
 #define MAX_OPTIONS 32
-static int setup_requirement_line(char *name)
-{
+static void check_requirement(Transport* transport, char* line) {
     char *val[MAX_OPTIONS];
-    char *prod = nullptr;
-    unsigned n, count;
+    unsigned count;
     char *x;
     int invert = 0;
 
+    // "require product=alpha|beta|gamma"
+    // "require version-bootloader=1234"
+    // "require-for-product:gamma version-bootloader=istanbul|constantinople"
+    // "require partition-exists=vendor"
+
+    char* name = line;
+    const char* product = "";
     if (!strncmp(name, "reject ", 7)) {
         name += 7;
         invert = 1;
@@ -662,19 +668,46 @@
         invert = 0;
     } else if (!strncmp(name, "require-for-product:", 20)) {
         // Get the product and point name past it
-        prod = name + 20;
+        product = name + 20;
         name = strchr(name, ' ');
-        if (!name) return -1;
+        if (!name) die("android-info.txt syntax error: %s", line);
         *name = 0;
         name += 1;
         invert = 0;
     }
 
     x = strchr(name, '=');
-    if (x == 0) return 0;
+    if (x == 0) return;
     *x = 0;
     val[0] = x + 1;
 
+    name = strip(name);
+
+    // "require partition-exists=x" is a special case, added because of the trouble we had when
+    // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
+    // missing out new partitions. A device with new partitions can use "partition-exists" to
+    // override the `is_optional` field in the `images` array.
+    if (!strcmp(name, "partition-exists")) {
+        const char* partition_name = val[0];
+        std::string has_slot;
+        if (!fb_getvar(transport, std::string("has-slot:") + partition_name, &has_slot) ||
+            (has_slot != "yes" && has_slot != "no")) {
+            die("device doesn't have required partition %s!", partition_name);
+        }
+        bool known_partition = false;
+        for (size_t i = 0; i < arraysize(images); ++i) {
+            if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
+                images[i].is_optional = false;
+                known_partition = true;
+            }
+        }
+        if (!known_partition) {
+            die("device requires partition %s which is not known to this version of fastboot",
+                partition_name);
+        }
+        return;
+    }
+
     for(count = 1; count < MAX_OPTIONS; count++) {
         x = strchr(val[count - 1],'|');
         if (x == 0) break;
@@ -682,54 +715,39 @@
         val[count] = x + 1;
     }
 
-    name = strip(name);
-    for(n = 0; n < count; n++) val[n] = strip(val[n]);
-
-    name = strip(name);
-    if (name == 0) return -1;
-
-    const char* var = name;
     // Work around an unfortunate name mismatch.
-    if (!strcmp(name,"board")) var = "product";
+    const char* var = name;
+    if (!strcmp(name, "board")) var = "product";
 
     const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
-    if (out == 0) return -1;
+    if (out == nullptr) die("out of memory");
 
-    for(n = 0; n < count; n++) {
-        out[n] = strdup(strip(val[n]));
-        if (out[n] == 0) {
-            for(size_t i = 0; i < n; ++i) {
-                free((char*) out[i]);
-            }
-            free(out);
-            return -1;
-        }
+    for (size_t i = 0; i < count; ++i) {
+        out[i] = xstrdup(strip(val[i]));
     }
 
-    fb_queue_require(prod, var, invert, n, out);
-    return 0;
+    fb_queue_require(product, var, invert, count, out);
 }
 
-static void setup_requirements(char* data, int64_t sz) {
+static void check_requirements(Transport* transport, char* data, int64_t sz) {
     char* s = data;
     while (sz-- > 0) {
         if (*s == '\n') {
             *s++ = 0;
-            if (setup_requirement_line(data)) {
-                die("out of memory");
-            }
+            check_requirement(transport, data);
             data = s;
         } else {
             s++;
         }
     }
+    if (fb_execute_queue(transport)) die("requirements not met!");
 }
 
 static void queue_info_dump() {
     fb_queue_notice("--------------------------------------------");
-    fb_queue_display("version-bootloader", "Bootloader Version...");
-    fb_queue_display("version-baseband",   "Baseband Version.....");
-    fb_queue_display("serialno",           "Serial Number........");
+    fb_queue_display("Bootloader Version...", "version-bootloader");
+    fb_queue_display("Baseband Version.....", "version-baseband");
+    fb_queue_display("Serial Number........", "serialno");
     fb_queue_notice("--------------------------------------------");
 }
 
@@ -889,14 +907,13 @@
     lseek(fd, 0, SEEK_SET);
 }
 
-static void flash_buf(const char *pname, struct fastboot_buffer *buf)
+static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
 {
     sparse_file** s;
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
     if ((g_disable_verity || g_disable_verification) &&
-        (strcmp(pname, "vbmeta") == 0 || strcmp(pname, "vbmeta_a") == 0 ||
-         strcmp(pname, "vbmeta_b") == 0)) {
+        (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b")) {
         rewrite_vbmeta_buffer(buf);
     }
 
@@ -912,12 +929,12 @@
 
             for (size_t i = 0; i < sparse_files.size(); ++i) {
                 const auto& pair = sparse_files[i];
-                fb_queue_flash_sparse(pname, pair.first, pair.second, i + 1, sparse_files.size());
+                fb_queue_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
             }
             break;
         }
         case FB_BUFFER_FD:
-            fb_queue_flash_fd(pname, buf->fd, buf->sz);
+            fb_queue_flash_fd(partition, buf->fd, buf->sz);
             break;
         default:
             die("unknown buffer type: %d", buf->type);
@@ -1115,11 +1132,11 @@
         }
     }
     if (slot_override != "") {
-        fb_set_active((separator + slot_override).c_str());
+        fb_set_active(separator + slot_override);
     } else {
         std::string current_slot = get_current_slot(transport);
         if (current_slot != "") {
-            fb_set_active((separator + current_slot).c_str());
+            fb_set_active(separator + current_slot);
         }
     }
 }
@@ -1141,7 +1158,7 @@
         die("update package '%s' has no android-info.txt", filename);
     }
 
-    setup_requirements(reinterpret_cast<char*>(data), sz);
+    check_requirements(transport, reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
@@ -1183,7 +1200,7 @@
         auto update = [&](const std::string& partition) {
             do_update_signature(zip, images[i].sig_name);
             if (erase_first && needs_erase(transport, partition.c_str())) {
-                fb_queue_erase(partition.c_str());
+                fb_queue_erase(partition);
             }
             flash_buf(partition.c_str(), &buf);
             /* not closing the fd here since the sparse code keeps the fd around
@@ -1230,7 +1247,7 @@
     void* data = load_file(fname.c_str(), &sz);
     if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
 
-    setup_requirements(reinterpret_cast<char*>(data), sz);
+    check_requirements(transport, reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
@@ -1265,7 +1282,7 @@
         auto flashall = [&](const std::string &partition) {
             do_send_signature(fname.c_str());
             if (erase_first && needs_erase(transport, partition.c_str())) {
-                fb_queue_erase(partition.c_str());
+                fb_queue_erase(partition);
             }
             flash_buf(partition.c_str(), &buf);
         };
@@ -1305,7 +1322,7 @@
     while (!args->empty()) {
         command += " " + next_arg(args);
     }
-    fb_queue_command(command.c_str(), "");
+    fb_queue_command(command, "");
 }
 
 static int64_t parse_num(const char *arg)
@@ -1360,8 +1377,8 @@
 
 static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
     std::string sizeString;
-    if (!fb_getvar(transport, name.c_str(), &sizeString) || sizeString.empty()) {
-        /* This device does not report flash block sizes, so return 0 */
+    if (!fb_getvar(transport, name, &sizeString) || sizeString.empty()) {
+        // This device does not report flash block sizes, so return 0.
         return 0;
     }
     sizeString = fb_fix_numeric_var(sizeString);
@@ -1379,7 +1396,7 @@
 }
 
 static void fb_perform_format(Transport* transport,
-                              const char* partition, int skip_if_not_supported,
+                              const std::string& partition, int skip_if_not_supported,
                               const std::string& type_override, const std::string& size_override,
                               const std::string& initial_dir) {
     std::string partition_type, partition_size;
@@ -1398,26 +1415,26 @@
         limit = sparse_limit;
     }
 
-    if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
+    if (!fb_getvar(transport, "partition-type:" + partition, &partition_type)) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
     if (!type_override.empty()) {
         if (partition_type != type_override) {
             fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
-                    partition, partition_type.c_str(), type_override.c_str());
+                    partition.c_str(), partition_type.c_str(), type_override.c_str());
         }
         partition_type = type_override;
     }
 
-    if (!fb_getvar(transport, std::string("partition-size:") + partition, &partition_size)) {
+    if (!fb_getvar(transport, "partition-size:" + partition, &partition_size)) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
     if (!size_override.empty()) {
         if (partition_size != size_override) {
             fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
-                    partition, partition_size.c_str(), size_override.c_str());
+                    partition.c_str(), partition_size.c_str(), size_override.c_str());
         }
         partition_size = size_override;
     }
@@ -1447,7 +1464,7 @@
 
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
-        die("Cannot generate image for %s", partition);
+        die("Cannot generate image for %s", partition.c_str());
         return;
     }
 
@@ -1630,6 +1647,8 @@
         return 1;
     }
 
+    const double start = now();
+
     if (!supports_AB(transport) && supports_AB_obsolete(transport)) {
         fprintf(stderr, "Warning: Device A/B support is outdated. Bootloader update required.\n");
     }
@@ -1657,7 +1676,7 @@
 
         if (command == "getvar") {
             std::string variable = next_arg(&args);
-            fb_queue_display(variable.c_str(), variable.c_str());
+            fb_queue_display(variable, variable);
         } else if (command == "erase") {
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
@@ -1669,7 +1688,7 @@
                             partition_type.c_str());
                 }
 
-                fb_queue_erase(partition.c_str());
+                fb_queue_erase(partition);
             };
             do_for_partitions(transport, partition, slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
@@ -1690,10 +1709,9 @@
 
             auto format = [&](const std::string& partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
-                    fb_queue_erase(partition.c_str());
+                    fb_queue_erase(partition);
                 }
-                fb_perform_format(transport, partition.c_str(), 0, type_override, size_override,
-                                  "");
+                fb_perform_format(transport, partition, 0, type_override, size_override, "");
             };
             do_for_partitions(transport, partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
@@ -1747,7 +1765,7 @@
 
             auto flash = [&](const std::string &partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
-                    fb_queue_erase(partition.c_str());
+                    fb_queue_erase(partition);
                 }
                 do_flash(transport, partition.c_str(), fname.c_str());
             };
@@ -1762,7 +1780,7 @@
 
             data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
             auto flashraw = [&](const std::string& partition) {
-                fb_queue_flash(partition.c_str(), data, sz);
+                fb_queue_flash(partition, data, sz);
             };
             do_for_partitions(transport, partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
@@ -1796,7 +1814,7 @@
                     fb_getvar(transport, "slot-suffixes", &var)) {
                 slot = "_" + slot;
             }
-            fb_set_active(slot.c_str());
+            fb_set_active(slot);
         } else if (command == "stage") {
             std::string filename = next_arg(&args);
 
@@ -1804,10 +1822,10 @@
             if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
-            fb_queue_download_fd(filename.c_str(), buf.fd, buf.sz);
+            fb_queue_download_fd(filename, buf.fd, buf.sz);
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
-            fb_queue_upload(filename.c_str());
+            fb_queue_upload(filename);
         } else if (command == "oem") {
             do_oem_command("oem", &args);
         } else if (command == "flashing") {
@@ -1853,7 +1871,7 @@
         }
     }
     if (wants_set_active) {
-        fb_set_active(next_active.c_str());
+        fb_set_active(next_active);
     }
     if (wants_reboot && !skip_reboot) {
         fb_queue_reboot();
@@ -1866,5 +1884,7 @@
         fb_queue_wait_for_disconnect();
     }
 
-    return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
+    int status = fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
+    fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
+    return status;
 }
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index f4faa21..a31057a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -39,8 +39,8 @@
 struct sparse_file;
 
 /* protocol.c - fastboot protocol */
-int fb_command(Transport* transport, const char* cmd);
-int fb_command_response(Transport* transport, const char* cmd, char* response);
+int fb_command(Transport* transport, const std::string& cmd);
+int fb_command_response(Transport* transport, const std::string& cmd, char* response);
 int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
 int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
 int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
@@ -52,29 +52,29 @@
 
 /* engine.c - high level command queue engine */
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
-void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
-void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz);
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
-                           size_t total);
-void fb_queue_erase(const char *ptn);
-void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz);
-void fb_queue_require(const char *prod, const char *var, bool invert,
-                      size_t nvalues, const char **value);
-void fb_queue_display(const char *var, const char *prettyname);
-void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size);
+void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
+void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
+void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
+                           size_t current, size_t total);
+void fb_queue_erase(const std::string& partition);
+void fb_queue_format(const std::string& partition, int skip_if_not_supported, int32_t max_chunk_sz);
+void fb_queue_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
+                      const char** values);
+void fb_queue_display(const std::string& label, const std::string& var);
+void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size);
 void fb_queue_reboot(void);
-void fb_queue_command(const char *cmd, const char *msg);
-void fb_queue_download(const char *name, void *data, uint32_t size);
-void fb_queue_download_fd(const char *name, int fd, uint32_t sz);
-void fb_queue_upload(const char* outfile);
-void fb_queue_notice(const char *notice);
+void fb_queue_command(const std::string& cmd, const std::string& msg);
+void fb_queue_download(const std::string& name, void* data, uint32_t size);
+void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz);
+void fb_queue_upload(const std::string& outfile);
+void fb_queue_notice(const std::string& notice);
 void fb_queue_wait_for_disconnect(void);
 int64_t fb_execute_queue(Transport* transport);
-void fb_set_active(const char *slot);
+void fb_set_active(const std::string& slot);
 
 /* util stuff */
 double now();
-char *mkmsg(const char *fmt, ...);
+char* xstrdup(const char*);
 
 // These printf-like functions are implemented in terms of vsnprintf, so they
 // use the same attribute for compile-time format string checking. On Windows,
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index dcdf8f0..c239861 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -113,10 +113,10 @@
     return -1;
 }
 
-static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
-    size_t cmdsize = strlen(cmd);
-    if (cmdsize > 64) {
-        g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
+static int64_t _command_start(Transport* transport, const std::string& cmd, uint32_t size,
+                              char* response) {
+    if (cmd.size() > 64) {
+        g_error = android::base::StringPrintf("command too large (%zu)", cmd.size());
         return -1;
     }
 
@@ -124,7 +124,7 @@
         response[0] = 0;
     }
 
-    if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
+    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
         g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
         transport->Close();
         return -1;
@@ -167,8 +167,8 @@
     return check_response(transport, 0, 0) < 0 ? -1 : 0;
 }
 
-static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
-                         char* response) {
+static int64_t _command_send(Transport* transport, const std::string& cmd, const void* data,
+                             uint32_t size, char* response) {
     if (size == 0) {
         return -1;
     }
@@ -190,7 +190,7 @@
     return size;
 }
 
-static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
+static int64_t _command_send_fd(Transport* transport, const std::string& cmd, int fd, uint32_t size,
                                 char* response) {
     static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
     off64_t offset = 0;
@@ -223,15 +223,15 @@
     return size;
 }
 
-static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
+static int _command_send_no_data(Transport* transport, const std::string& cmd, char* response) {
     return _command_start(transport, cmd, 0, response);
 }
 
-int fb_command(Transport* transport, const char* cmd) {
+int fb_command(Transport* transport, const std::string& cmd) {
     return _command_send_no_data(transport, cmd, 0);
 }
 
-int fb_command_response(Transport* transport, const char* cmd, char* response) {
+int fb_command_response(Transport* transport, const std::string& cmd, char* response) {
     return _command_send_no_data(transport, cmd, response);
 }
 
@@ -339,7 +339,7 @@
     }
 
     std::string cmd(android::base::StringPrintf("download:%08x", size));
-    int r = _command_start(transport, cmd.c_str(), size, 0);
+    int r = _command_start(transport, cmd, size, 0);
     if (r < 0) {
         return -1;
     }
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index f2bbd34..fb085e7 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -35,35 +35,24 @@
 
 #include "fastboot.h"
 
-double now()
-{
+double now() {
     struct timeval tv;
     gettimeofday(&tv, NULL);
     return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
 }
 
-char *mkmsg(const char *fmt, ...)
-{
-    char buf[256];
-    char *s;
-    va_list ap;
-
-    va_start(ap, fmt);
-    vsprintf(buf, fmt, ap);
-    va_end(ap);
-
-    s = strdup(buf);
-    if (s == 0) die("out of memory");
-    return s;
-}
-
-void die(const char *fmt, ...)
-{
+void die(const char* fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr,"error: ");
     vfprintf(stderr, fmt, ap);
     fprintf(stderr,"\n");
     va_end(ap);
-    exit(1);
+    exit(EXIT_FAILURE);
+}
+
+char* xstrdup(const char* s) {
+    char* result = strdup(s);
+    if (!result) die("out of memory");
+    return result;
 }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 1c01d8c..72a65d2 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -33,12 +33,15 @@
 
 #include "fs_mgr_priv.h"
 
+using android::base::StartsWith;
+
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
 
 struct fs_mgr_flag_values {
     char *key_loc;
     char* key_dir;
     char *verity_loc;
+    char *sysfs_path;
     long long part_length;
     char *label;
     int partnum;
@@ -104,6 +107,7 @@
     {"quota", MF_QUOTA},
     {"eraseblk=", MF_ERASEBLKSIZE},
     {"logicalblk=", MF_LOGICALBLKSIZE},
+    {"sysfs_path=", MF_SYSFS},
     {"defaults", 0},
     {0, 0},
 };
@@ -341,6 +345,9 @@
                     unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->logical_blk_size = val;
+                } else if ((fl[i].flag == MF_SYSFS) && flag_vals) {
+                    /* The path to trigger device gc by idle-maint of vold. */
+                    flag_vals->sysfs_path = strdup(strchr(p, '=') + 1);
                 }
                 break;
             }
@@ -434,6 +441,10 @@
             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
             return {};
         }
+        if (!StartsWith(value, "/dev")) {
+            LERROR << "dt_fstab: Invalid device node for partition " << dp->d_name;
+            return {};
+        }
         fstab_entry.push_back(value);
 
         std::string mount_point;
@@ -615,6 +626,7 @@
         fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
         fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
         fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
+        fstab->recs[cnt].sysfs_path = flag_vals.sysfs_path;
         cnt++;
     }
     /* If an A/B partition, modify block device to be the real block device */
@@ -787,6 +799,7 @@
         free(fstab->recs[i].key_loc);
         free(fstab->recs[i].key_dir);
         free(fstab->recs[i].label);
+        free(fstab->recs[i].sysfs_path);
     }
 
     /* Free the fstab_recs array created by calloc(3) */
@@ -922,3 +935,8 @@
 int fs_mgr_is_quota(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_QUOTA;
 }
+
+int fs_mgr_has_sysfs_path(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_SYSFS;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 724156d..ade0cc4 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -109,7 +109,8 @@
 #define MF_ERASEBLKSIZE     0x800000
 #define MF_LOGICALBLKSIZE  0X1000000
 #define MF_AVB             0X2000000
-#define MF_KEYDIRECTORY 0X4000000
+#define MF_KEYDIRECTORY    0X4000000
+#define MF_SYSFS           0X8000000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 94aacfd..8c585dd 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -56,6 +56,7 @@
     unsigned int file_names_mode;
     unsigned int erase_blk_size;
     unsigned int logical_blk_size;
+    char* sysfs_path;
 };
 
 struct fstab* fs_mgr_read_fstab_default();
@@ -83,6 +84,7 @@
 int fs_mgr_is_nofail(const struct fstab_rec* fstab);
 int fs_mgr_is_latemount(const struct fstab_rec* fstab);
 int fs_mgr_is_quota(const struct fstab_rec* fstab);
+int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
 
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..6c8fecf 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -98,9 +98,6 @@
 ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
 LOCAL_CHARGER_NO_UI := true
 endif
-ifdef BRILLO
-LOCAL_CHARGER_NO_UI := true
-endif
 
 LOCAL_SRC_FILES := \
     healthd_common.cpp \
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 08b8b26..fa79d0b 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -143,7 +143,7 @@
 
 BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
     std::string buf;
-    BatteryMonitor::PowerSupplyType ret;
+    int ret;
     struct sysfsStringEnumMap supplyTypeMap[] = {
             { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
             { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
@@ -164,13 +164,13 @@
     if (readFromFile(path, &buf) <= 0)
         return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
 
-    ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf.c_str(), supplyTypeMap);
+    ret = mapSysfsString(buf.c_str(), supplyTypeMap);
     if (ret < 0) {
         KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
         ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
     }
 
-    return ret;
+    return static_cast<BatteryMonitor::PowerSupplyType>(ret);
 }
 
 bool BatteryMonitor::getBooleanField(const String8& path) {
diff --git a/init/Android.bp b/init/Android.bp
index 69b4ee4..31c8efb 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -194,4 +194,67 @@
     static_libs: ["libinit"],
 }
 
+// Host Verifier
+// ------------------------------------------------------------------------------
+
+genrule {
+    name: "generated_stub_builtin_function_map",
+    out: ["generated_stub_builtin_function_map.h"],
+    srcs: ["builtins.cpp"],
+    cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
+}
+
+cc_binary {
+    name: "host_init_verifier",
+    host_supported: true,
+    cpp_std: "experimental",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Werror",
+    ],
+    static_libs: [
+        "libbase",
+        "libselinux",
+    ],
+    whole_static_libs: ["libcap"],
+    shared_libs: [
+        "libprotobuf-cpp-lite",
+        "libhidl-gen-utils",
+        "libprocessgroup",
+        "liblog",
+        "libcutils",
+    ],
+    srcs: [
+        "action.cpp",
+        "action_manager.cpp",
+        "action_parser.cpp",
+        "capabilities.cpp",
+        "descriptors.cpp",
+        "import_parser.cpp",
+        "host_init_parser.cpp",
+        "host_init_stubs.cpp",
+        "parser.cpp",
+        "rlimit_parser.cpp",
+        "tokenizer.cpp",
+        "service.cpp",
+        "subcontext.cpp",
+        "subcontext.proto",
+        "util.cpp",
+    ],
+    proto: {
+        type: "lite",
+    },
+    generated_headers: ["generated_stub_builtin_function_map"],
+    target: {
+        android: {
+            enabled: false,
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
 subdirs = ["*"]
diff --git a/init/README.md b/init/README.md
index d7edf21..59ddd77 100644
--- a/init/README.md
+++ b/init/README.md
@@ -10,7 +10,11 @@
 whitespace from breaking text into multiple tokens.  The backslash,
 when it is the last character on a line, may be used for line-folding.
 
-Lines which start with a # (leading whitespace allowed) are comments.
+Lines which start with a `#` (leading whitespace allowed) are comments.
+
+System properties can be expanded using the syntax
+`${property.name}`. This also works in contexts where concatenation is
+required, such as `import /init.recovery.${ro.hardware}.rc`.
 
 Actions and Services implicitly declare a new section.  All commands
 or options belong to the section most recently declared.  Commands
@@ -157,6 +161,25 @@
 Options are modifiers to services.  They affect how and when init
 runs the service.
 
+`capabilities <capability> [ <capability>\* ]`
+> Set capabilities when exec'ing this service. 'capability' should be a Linux
+  capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
+  http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
+  capabilities.
+
+`class <name> [ <name>\* ]`
+> Specify class names for the service.  All services in a
+  named class may be started or stopped together.  A service
+  is in the class "default" if one is not specified via the
+  class option. Additional classnames beyond the (required) first
+  one are used to group services.
+  The `animation` class should include all services necessary for both
+  boot animation and shutdown animation. As these services can be
+  launched very early during bootup and can run until the last stage
+  of shutdown, access to /data partition is not guaranteed. These
+  services can check files under /data but it should not keep files opened
+  and should work when /data is not available.
+
 `console [<console>]`
 > This service needs a console. The optional second parameter chooses a
   specific console instead of the default. The default "/dev/console" can
@@ -172,9 +195,93 @@
 > This service will not automatically start with its class.
   It must be explicitly started by name.
 
+`file <path> <type>`
+> Open a file path and pass its fd to the launched process. _type_ must be
+  "r", "w" or "rw".  For native executables see libcutils
+  android\_get\_control\_file().
+
+`group <groupname> [ <groupname>\* ]`
+> Change to 'groupname' before exec'ing this service.  Additional
+  groupnames beyond the (required) first one are used to set the
+  supplemental groups of the process (via setgroups()).
+  Currently defaults to root.  (??? probably should default to nobody)
+
+`interface <interface name> <instance name>`
+> Associates this service with a list of the HIDL services that it provides. The interface name
+  must be a fully-qualified name and not a value name. This is used to allow hwservicemanager to
+  lazily start services.
+  For example: interface vendor.foo.bar@1.0::IBaz default
+
+`ioprio <class> <priority>`
+> Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall.
+  _class_ must be one of "rt", "be", or "idle". _priority_ must be an integer in the range 0 - 7.
+
+`keycodes <keycode> [ <keycode>\* ]`
+> Sets the keycodes that will trigger this service. If all of the keys corresponding to the passed
+  keycodes are pressed at once, the service will start. This is typically used to start the
+  bugreport service.
+
+`memcg.limit_in_bytes <value>`
+> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
+  which must be equal or greater than 0.
+
+`memcg.soft_limit_in_bytes <value>`
+> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
+  which must be equal or greater than 0.
+
+`memcg.swappiness <value>`
+> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
+  which must be equal or greater than 0.
+
+`namespace <pid|mnt>`
+> Enter a new PID or mount namespace when forking the service.
+
+`oneshot`
+> Do not restart the service when it exits.
+
+`onrestart`
+> Execute a Command (see below) when service restarts.
+
+`oom_score_adjust <value>`
+> Sets the child's /proc/self/oom\_score\_adj to the specified value,
+  which must range from -1000 to 1000.
+
+`override`
+> Indicates that this service definition is meant to override a previous definition for a service
+  with the same name. This is typically meant for services on /odm to override those defined on
+  /vendor. The last service definition that init parses with this keyword is the service definition
+  will use for this service. Pay close attention to the order in which init.rc files are parsed,
+  since it has some peculiarities for backwards compatibility reasons. The 'imports' section of
+  this file has more details on the order.
+
+`priority <priority>`
+> Scheduling priority of the service process. This value has to be in range
+  -20 to 19. Default priority is 0. Priority is set via setpriority().
+
+`rlimit <resource> <cur> <max>`
+> This applies the given rlimit to the service. rlimits are inherited by child
+  processes, so this effectively applies the given rlimit to the process tree
+  started by this service.
+  It is parsed similarly to the setrlimit command specified below.
+
+`seclabel <seclabel>`
+> Change to 'seclabel' before exec'ing this service.
+  Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
+  Services on the system partition can instead use policy-defined transitions
+  based on their file security context.
+  If not specified and no transition is defined in policy, defaults to the init context.
+
 `setenv <name> <value>`
 > Set the environment variable _name_ to _value_ in the launched process.
 
+`shutdown <shutdown_behavior>`
+> Set shutdown behavior of the service process. When this is not specified,
+  the service is killed during shutdown process by using SIGTERM and SIGKILL.
+  The service with shutdown_behavior of "critical" is not killed during shutdown
+  until shutdown times out. When shutdown times out, even services tagged with
+  "shutdown critical" will be killed. When the service tagged with "shutdown critical"
+  is not running when shut down starts, it will be started.
+
 `socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
 > Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
   launched process.  _type_ must be "dgram", "stream" or "seqpacket".  User and
@@ -183,11 +290,6 @@
   seclabel or computed based on the service executable file security context.
   For native executables see libcutils android\_get\_control\_socket().
 
-`file <path> <type>`
-> Open a file path and pass its fd to the launched process. _type_ must be
-  "r", "w" or "rw".  For native executables see libcutils
-  android\_get\_control\_file().
-
 `user <username>`
 > Change to 'username' before exec'ing this service.
   Currently defaults to root.  (??? probably should default to nobody)
@@ -204,88 +306,12 @@
   As of Android O, processes can also request capabilities directly in their .rc
   files. See the "capabilities" option below.
 
-`group <groupname> [ <groupname>\* ]`
-> Change to 'groupname' before exec'ing this service.  Additional
-  groupnames beyond the (required) first one are used to set the
-  supplemental groups of the process (via setgroups()).
-  Currently defaults to root.  (??? probably should default to nobody)
-
-`capabilities <capability> [ <capability>\* ]`
-> Set capabilities when exec'ing this service. 'capability' should be a Linux
-  capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
-  http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
-  capabilities.
-
-`setrlimit <resource> <cur> <max>`
-> This applies the given rlimit to the service. rlimits are inherited by child
-  processes, so this effectively applies the given rlimit to the process tree
-  started by this service.
-  It is parsed similarly to the setrlimit command specified below.
-
-`seclabel <seclabel>`
-> Change to 'seclabel' before exec'ing this service.
-  Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
-  Services on the system partition can instead use policy-defined transitions
-  based on their file security context.
-  If not specified and no transition is defined in policy, defaults to the init context.
-
-`oneshot`
-> Do not restart the service when it exits.
-
-`class <name> [ <name>\* ]`
-> Specify class names for the service.  All services in a
-  named class may be started or stopped together.  A service
-  is in the class "default" if one is not specified via the
-  class option. Additional classnames beyond the (required) first
-  one are used to group services.
-`animation class`
-> 'animation' class should include all services necessary for both
-  boot animation and shutdown animation. As these services can be
-  launched very early during bootup and can run until the last stage
-  of shutdown, access to /data partition is not guaranteed. These
-  services can check files under /data but it should not keep files opened
-  and should work when /data is not available.
-
-`onrestart`
-> Execute a Command (see below) when service restarts.
-
 `writepid <file> [ <file>\* ]`
 > Write the child's pid to the given files when it forks. Meant for
   cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
   system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
   '/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
 
-`priority <priority>`
-> Scheduling priority of the service process. This value has to be in range
-  -20 to 19. Default priority is 0. Priority is set via setpriority().
-
-`namespace <pid|mnt>`
-> Enter a new PID or mount namespace when forking the service.
-
-`oom_score_adjust <value>`
-> Sets the child's /proc/self/oom\_score\_adj to the specified value,
-  which must range from -1000 to 1000.
-
-`memcg.swappiness <value>`
-> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
-
-`memcg.soft_limit_in_bytes <value>`
-> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
-
-`memcg.limit_in_bytes <value>`
-> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
-
-`shutdown <shutdown_behavior>`
-> Set shutdown behavior of the service process. When this is not specified,
-  the service is killed during shutdown process by using SIGTERM and SIGKILL.
-  The service with shutdown_behavior of "critical" is not killed during shutdown
-  until shutdown times out. When shutdown times out, even services tagged with
-  "shutdown critical" will be killed. When the service tagged with "shutdown critical"
-  is not running when shut down starts, it will be started.
-
 
 Triggers
 --------
diff --git a/init/action.cpp b/init/action.cpp
index 11335ca..f782b51 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,11 +18,16 @@
 
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
-#include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #include "util.h"
 
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
 using android::base::Join;
 
 namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 8a4b518..a2c9671 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,11 +16,16 @@
 
 #include "action_parser.h"
 
-#include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #include "stable_properties.h"
 
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
 using android::base::GetBoolProperty;
 using android::base::StartsWith;
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1040b47..fc74dda 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -285,8 +285,11 @@
 
     if (e4crypt_is_native()) {
         if (e4crypt_set_directory_policy(args[1].c_str())) {
-            reboot_into_recovery(
-                {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
+            const std::vector<std::string> options = {
+                "--prompt_and_wipe_data",
+                "--reason=set_policy_failed:"s + args[1]};
+            reboot_into_recovery(options);
+            return Success();
         }
     }
     return Success();
@@ -968,8 +971,8 @@
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
 
-    if (!is_legal_property_name(name)) {
-        return Error() << "is_legal_property_name(" << name << ") failed";
+    if (!IsLegalPropertyName(name)) {
+        return Error() << "IsLegalPropertyName(" << name << ") failed";
     }
     if (value_len >= PROP_VALUE_MAX) {
         return Error() << "value too long";
@@ -984,24 +987,6 @@
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
-                                               const std::vector<std::string>& args) {
-    auto service = Service::MakeTemporaryOneshotService(args);
-    if (!service) {
-        return Error() << "Could not create exec service";
-    }
-    service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
-        if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
-        }
-    });
-    if (auto result = service->ExecStart(); !result) {
-        return Error() << "Could not start exec service: " << result.error();
-    }
-    ServiceList::GetInstance().AddService(std::move(service));
-    return Success();
-}
-
 static Result<Success> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return Success();
 
@@ -1009,15 +994,18 @@
     if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
         return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
-    return ExecWithRebootOnFailure("enablefilecrypto_failed", {"exec", "/system/bin/vdc", "--wait",
-                                                               "cryptfs", "enablefilecrypto"});
+    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+                                          "enablefilecrypto"};
+    return do_exec({std::move(exec_args), args.context});
 }
 
 static Result<Success> do_init_user0(const BuiltinArguments& args) {
-    return ExecWithRebootOnFailure("init_user0_failed",
-                                   {"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"});
+    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+                                          "init_user0"};
+    return do_exec({std::move(exec_args), args.context});
 }
 
+// Builtin-function-map start
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
@@ -1075,6 +1063,7 @@
     // clang-format on
     return builtin_functions;
 }
+// Builtin-function-map end
 
 }  // namespace init
 }  // namespace android
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 50987db..a91cd1d 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -14,7 +14,6 @@
 
 #include "capabilities.h"
 
-#include <sys/capability.h>
 #include <sys/prctl.h>
 
 #include <map>
@@ -72,10 +71,15 @@
 static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
 
 static bool ComputeCapAmbientSupported() {
+#if defined(__ANDROID__)
     return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+#else
+    return true;
+#endif
 }
 
 static unsigned int ComputeLastValidCap() {
+#if defined(__ANDROID__)
     // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
     // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
     unsigned int last_valid_cap = CAP_WAKE_ALARM;
@@ -83,6 +87,9 @@
 
     // |last_valid_cap| will be the first failing value.
     return last_valid_cap - 1;
+#else
+    return CAP_LAST_CAP;
+#endif
 }
 
 static bool DropBoundingSet(const CapSet& to_keep) {
@@ -139,6 +146,7 @@
 }
 
 static bool SetAmbientCaps(const CapSet& to_raise) {
+#if defined(__ANDROID__)
     for (size_t cap = 0; cap < to_raise.size(); ++cap) {
         if (to_raise.test(cap)) {
             if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@@ -147,6 +155,7 @@
             }
         }
     }
+#endif
     return true;
 }
 
diff --git a/init/capabilities.h b/init/capabilities.h
index fc80c98..891e0ac 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -21,6 +21,17 @@
 #include <string>
 #include <type_traits>
 
+#if !defined(__ANDROID__)
+#ifndef CAP_BLOCK_SUSPEND
+#define CAP_BLOCK_SUSPEND 36
+#endif
+#ifndef CAP_AUDIT_READ
+#define CAP_AUDIT_READ 37
+#endif
+#undef CAP_LAST_CAP
+#define CAP_LAST_CAP CAP_AUDIT_READ
+#endif
+
 namespace android {
 namespace init {
 
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
new file mode 100644
index 0000000..5232b7e
--- /dev/null
+++ b/init/host_init_parser.cpp
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <pwd.h>
+
+#include <android-base/logging.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+// The host passwd file won't have the Android entries, so we fake success here.
+passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
+    char dummy_buf[] = "dummy";
+    static passwd dummy_passwd = {
+        .pw_name = dummy_buf,
+        .pw_dir = dummy_buf,
+        .pw_shell = dummy_buf,
+        .pw_uid = 123,
+        .pw_gid = 123,
+    };
+    return &dummy_passwd;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+    return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    if (argc != 2) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
+        return -1;
+    }
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceList& sl = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+
+    size_t num_errors = 0;
+    if (!parser.ParseConfig(argv[1], &num_errors)) {
+        LOG(ERROR) << "Failed to find script";
+        return -1;
+    }
+    if (num_errors > 0) {
+        LOG(ERROR) << "Parse failed with " << num_errors << " errors";
+        return -1;
+    }
+    LOG(INFO) << "Parse success!";
+    return 0;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::init::main(argc, argv);
+}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
new file mode 100644
index 0000000..e6cc08a
--- /dev/null
+++ b/init/host_init_stubs.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host_init_stubs.h"
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list) {
+    return 0;
+}
+
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string&, const std::string& default_value) {
+    return default_value;
+}
+
+bool GetBoolProperty(const std::string&, bool default_value) {
+    return default_value;
+}
+
+}  // namespace base
+}  // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+std::string default_console = "/dev/console";
+
+// property_service.h
+uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
+                           std::string*) {
+    return 0;
+}
+
+// selinux.h
+void SelabelInitialize() {}
+
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+    return false;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
new file mode 100644
index 0000000..f31ece6
--- /dev/null
+++ b/init/host_init_stubs.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_HOST_INIT_STUBS_H
+#define _INIT_HOST_INIT_STUBS_H
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <string>
+
+// sys/system_properties.h
+#define PROP_VALUE_MAX 92
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list);
+
+// android-base/properties.h
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string& key, const std::string& default_value);
+bool GetBoolProperty(const std::string& key, bool default_value);
+
+}  // namespace base
+}  // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+extern std::string default_console;
+
+// property_service.h
+extern uint32_t (*property_set)(const std::string& name, const std::string& value);
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+                           const std::string& source_context, const ucred& cr, std::string* error);
+
+// selinux.h
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 4c69bac..4453aaa 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
     line_callbacks_.emplace_back(prefix, callback);
 }
 
-void Parser::ParseData(const std::string& filename, const std::string& data) {
+void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
     // TODO: Use a parser with const input and remove this copy
     std::vector<char> data_copy(data.begin(), data.end());
     data_copy.push_back('\0');
@@ -57,6 +57,7 @@
         if (section_parser == nullptr) return;
 
         if (auto result = section_parser->EndSection(); !result) {
+            (*parse_errors)++;
             LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
         }
 
@@ -80,6 +81,7 @@
                         end_section();
 
                         if (auto result = callback(std::move(args)); !result) {
+                            (*parse_errors)++;
                             LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         }
                         break;
@@ -92,12 +94,14 @@
                     if (auto result =
                             section_parser->ParseSection(std::move(args), filename, state.line);
                         !result) {
+                        (*parse_errors)++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                         !result) {
+                        (*parse_errors)++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
                 }
@@ -110,7 +114,7 @@
     }
 }
 
-bool Parser::ParseConfigFile(const std::string& path) {
+bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
     auto config_contents = ReadFile(path);
@@ -120,7 +124,7 @@
     }
 
     config_contents->push_back('\n');  // TODO: fix parse_config.
-    ParseData(path, *config_contents);
+    ParseData(path, *config_contents, parse_errors);
     for (const auto& [section_name, section_parser] : section_parsers_) {
         section_parser->EndFile();
     }
@@ -129,7 +133,7 @@
     return true;
 }
 
-bool Parser::ParseConfigDir(const std::string& path) {
+bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
     LOG(INFO) << "Parsing directory " << path << "...";
     std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
     if (!config_dir) {
@@ -149,7 +153,7 @@
     // Sort first so we load files in a consistent order (bug 31996208)
     std::sort(files.begin(), files.end());
     for (const auto& file : files) {
-        if (!ParseConfigFile(file)) {
+        if (!ParseConfigFile(file, parse_errors)) {
             LOG(ERROR) << "could not import file '" << file << "'";
         }
     }
@@ -157,10 +161,16 @@
 }
 
 bool Parser::ParseConfig(const std::string& path) {
+    size_t parse_errors;
+    return ParseConfig(path, &parse_errors);
+}
+
+bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
+    *parse_errors = 0;
     if (is_dir(path.c_str())) {
-        return ParseConfigDir(path);
+        return ParseConfigDir(path, parse_errors);
     }
-    return ParseConfigFile(path);
+    return ParseConfigFile(path, parse_errors);
 }
 
 }  // namespace init
diff --git a/init/parser.h b/init/parser.h
index 110a468..f6e237f 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,13 +72,14 @@
     Parser();
 
     bool ParseConfig(const std::string& path);
+    bool ParseConfig(const std::string& path, size_t* parse_errors);
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
   private:
-    void ParseData(const std::string& filename, const std::string& data);
-    bool ParseConfigFile(const std::string& path);
-    bool ParseConfigDir(const std::string& path);
+    void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
+    bool ParseConfigFile(const std::string& path, size_t* parse_errors);
+    bool ParseConfigDir(const std::string& path, size_t* parse_errors);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
     std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 0cf6184..95ef35c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -59,8 +59,11 @@
 #include "init.h"
 #include "persistent_properties.h"
 #include "property_type.h"
+#include "subcontext.h"
 #include "util.h"
 
+using namespace std::literals;
+
 using android::base::ReadFileToString;
 using android::base::Split;
 using android::base::StartsWith;
@@ -117,48 +120,21 @@
     return has_access;
 }
 
-bool is_legal_property_name(const std::string& name) {
-    size_t namelen = name.size();
-
-    if (namelen < 1) return false;
-    if (name[0] == '.') return false;
-    if (name[namelen - 1] == '.') return false;
-
-    /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
-    /* Don't allow ".." to appear in a property name */
-    for (size_t i = 0; i < namelen; i++) {
-        if (name[i] == '.') {
-            // i=0 is guaranteed to never have a dot. See above.
-            if (name[i-1] == '.') return false;
-            continue;
-        }
-        if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
-        if (name[i] >= 'a' && name[i] <= 'z') continue;
-        if (name[i] >= 'A' && name[i] <= 'Z') continue;
-        if (name[i] >= '0' && name[i] <= '9') continue;
-        return false;
-    }
-
-    return true;
-}
-
-static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
+static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
     size_t valuelen = value.size();
 
-    if (!is_legal_property_name(name)) {
-        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
+    if (!IsLegalPropertyName(name)) {
+        *error = "Illegal property name";
         return PROP_ERROR_INVALID_NAME;
     }
 
     if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
-        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                   << "value too long";
+        *error = "Property value too long";
         return PROP_ERROR_INVALID_VALUE;
     }
 
     if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
-        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                   << "value not a UTF8 encoded string";
+        *error = "Value is not a UTF8 encoded string";
         return PROP_ERROR_INVALID_VALUE;
     }
 
@@ -166,8 +142,7 @@
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
         if (StartsWith(name, "ro.")) {
-            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                       << "property already set";
+            *error = "Read-only property was already set";
             return PROP_ERROR_READ_ONLY_PROPERTY;
         }
 
@@ -175,8 +150,7 @@
     } else {
         int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
         if (rc < 0) {
-            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                       << "__system_property_add failed";
+            *error = "__system_property_add failed";
             return PROP_ERROR_SET_FAILED;
         }
     }
@@ -230,8 +204,10 @@
     if (info.pid != pid) {
         return false;
     }
-    if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
-        LOG(ERROR) << "Failed to set async property " << info.name;
+    std::string error;
+    if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
+        LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
+                   << error;
     }
     property_children.pop();
     if (!property_children.empty()) {
@@ -241,9 +217,9 @@
 }
 
 static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
-                                 PropertyAsyncFunc func) {
+                                 PropertyAsyncFunc func, std::string* error) {
     if (value.empty()) {
-        return PropertySetImpl(name, value);
+        return PropertySet(name, value, error);
     }
 
     PropertyChildInfo info;
@@ -261,30 +237,27 @@
     return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
 }
 
-uint32_t PropertySet(const std::string& name, const std::string& value) {
-    if (name == "selinux.restorecon_recursive") {
-        return PropertySetAsync(name, value, RestoreconRecursiveAsync);
-    }
-
-    return PropertySetImpl(name, value);
-}
-
 uint32_t InitPropertySet(const std::string& name, const std::string& value) {
     if (StartsWith(name, "ctl.")) {
-        LOG(ERROR) << "Do not set ctl. properties from init; call the Service functions directly";
+        LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
+                      "functions directly";
+        return PROP_ERROR_INVALID_NAME;
+    }
+    if (name == "selinux.restorecon_recursive") {
+        LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
+                      "restorecon builtin directly";
         return PROP_ERROR_INVALID_NAME;
     }
 
-    const char* type = nullptr;
-    property_info_area->GetPropertyInfo(name.c_str(), nullptr, &type);
-
-    if (type == nullptr || !CheckType(type, value)) {
-        LOG(ERROR) << "property_set: name: '" << name << "' type check failed, type: '"
-                   << (type ?: "(null)") << "' value: '" << value << "'";
-        return PROP_ERROR_INVALID_VALUE;
+    uint32_t result = 0;
+    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+    std::string error;
+    result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
+    if (result != PROP_SUCCESS) {
+        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
     }
 
-    return PropertySet(name, value);
+    return result;
 }
 
 class SocketConnection {
@@ -415,9 +388,9 @@
 
 // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr) {
-    if (!is_legal_property_name(name)) {
-        LOG(ERROR) << "PropertySet: illegal property name \"" << name << "\"";
+                           const std::string& source_context, const ucred& cr, std::string* error) {
+    if (!IsLegalPropertyName(name)) {
+        *error = "Illegal property name";
         return PROP_ERROR_INVALID_NAME;
     }
 
@@ -430,9 +403,7 @@
         const char* type = nullptr;
         property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
         if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
-            LOG(ERROR) << "PropertySet: Unable to " << (name.c_str() + 4) << " service ctl ["
-                       << value << "]"
-                       << " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
+            *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
             return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
         }
 
@@ -445,13 +416,13 @@
     property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
 
     if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
-        LOG(ERROR) << "PropertySet: permission denied uid:" << cr.uid << " name:" << name;
+        *error = "SELinux permission check failed";
         return PROP_ERROR_PERMISSION_DENIED;
     }
 
     if (type == nullptr || !CheckType(type, value)) {
-        LOG(ERROR) << "PropertySet: name: '" << name << "' type check failed, type: '"
-                   << (type ?: "(null)") << "' value: '" << value << "'";
+        *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
+                              (type ?: "(null)"));
         return PROP_ERROR_INVALID_VALUE;
     }
 
@@ -470,7 +441,11 @@
                   << process_log_string;
     }
 
-    return PropertySet(name, value);
+    if (name == "selinux.restorecon_recursive") {
+        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+    }
+
+    return PropertySet(name, value, error);
 }
 
 static void handle_property_set_fd() {
@@ -513,7 +488,16 @@
         prop_name[PROP_NAME_MAX-1] = 0;
         prop_value[PROP_VALUE_MAX-1] = 0;
 
-        HandlePropertySet(prop_value, prop_value, socket.source_context(), socket.cred());
+        const auto& cr = socket.cred();
+        std::string error;
+        uint32_t result =
+            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+        if (result != PROP_SUCCESS) {
+            LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
+                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+                       << error;
+        }
+
         break;
       }
 
@@ -527,7 +511,14 @@
           return;
         }
 
-        auto result = HandlePropertySet(name, value, socket.source_context(), socket.cred());
+        const auto& cr = socket.cred();
+        std::string error;
+        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+        if (result != PROP_SUCCESS) {
+            LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
+                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+                       << error;
+        }
         socket.SendUint32(result);
         break;
       }
@@ -545,11 +536,17 @@
  * Filter is used to decide which properties to load: NULL loads all keys,
  * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
  */
-static void load_properties(char *data, const char *filter)
-{
+static void LoadProperties(char* data, const char* filter, const char* filename) {
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
+    const char* context = kInitContext.c_str();
+    for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+        if (StartsWith(filename, path_prefix)) {
+            context = secontext;
+        }
+    }
+
     if (filter) {
         flen = strlen(filter);
     }
@@ -596,7 +593,21 @@
                 }
             }
 
-            property_set(key, value);
+            if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
+                key == "selinux.restorecon_recursive"s) {
+                LOG(ERROR) << "Ignoring disallowed property '" << key
+                           << "' with special meaning in prop file '" << filename << "'";
+                continue;
+            }
+
+            uint32_t result = 0;
+            ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+            std::string error;
+            result = HandlePropertySet(key, value, context, cr, &error);
+            if (result != PROP_SUCCESS) {
+                LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+                           << "' in property file '" << filename << "': " << error;
+            }
         }
     }
 }
@@ -612,7 +623,8 @@
         return false;
     }
     file_contents->push_back('\n');
-    load_properties(file_contents->data(), filter);
+
+    LoadProperties(file_contents->data(), filter, filename);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
     return true;
 }
diff --git a/init/property_service.h b/init/property_service.h
index 8161b40..29eaaa9 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,7 +32,7 @@
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr);
+                           const std::string& source_context, const ucred& cr, std::string* error);
 
 extern bool PropertyChildReap(pid_t pid);
 
@@ -41,7 +41,6 @@
 void load_persist_props(void);
 void load_system_props(void);
 void start_property_service(void);
-bool is_legal_property_name(const std::string& name);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 242750a..6f6e39f 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -89,12 +89,13 @@
           mnt_opts_(entry.mnt_opts) {}
 
     bool Umount(bool force) {
+        LOG(INFO) << "Unmounting " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
         int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0);
         if (r == 0) {
-            LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
+            LOG(INFO) << "Umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
             return true;
         } else {
-            PLOG(WARNING) << "cannot umount " << mnt_fsname_ << ":" << mnt_dir_ << " opts "
+            PLOG(WARNING) << "Cannot umount " << mnt_fsname_ << ":" << mnt_dir_ << " opts "
                           << mnt_opts_;
             return false;
         }
@@ -457,10 +458,20 @@
         if (kill_after_apps.count(s->name())) s->Stop();
     }
     // 4. sync, try umount, and optionally run fsck for user shutdown
-    sync();
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() before umount...";
+        sync();
+        LOG(INFO) << "sync() before umount took" << sync_timer;
+    }
     UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
-    sync();
+    {
+        Timer sync_timer;
+        LOG(INFO) << "sync() after umount...";
+        sync();
+        LOG(INFO) << "sync() after umount took" << sync_timer;
+    }
     if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
     LogShutdownTime(stat, &t);
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
diff --git a/init/service.cpp b/init/service.cpp
index 35dd319..694e5e7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,7 +24,6 @@
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
-#include <sys/system_properties.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <termios.h>
@@ -33,8 +32,6 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <hidl-util/FQName.h>
@@ -42,15 +39,23 @@
 #include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
-#include "init.h"
-#include "property_service.h"
 #include "rlimit_parser.h"
 #include "util.h"
 
+#if defined(__ANDROID__)
+#include <sys/system_properties.h>
+
+#include <android-base/properties.h>
+
+#include "init.h"
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
+
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::Join;
-using android::base::make_scope_guard;
 using android::base::ParseInt;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -298,7 +303,7 @@
     }
 }
 
-void Service::Reap(const siginfo_t& siginfo) {
+void Service::Reap() {
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
         KillProcessGroup(SIGKILL);
     }
@@ -307,10 +312,6 @@
     std::for_each(descriptors_.begin(), descriptors_.end(),
                   std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
 
-    for (const auto& f : reap_callbacks_) {
-        f(siginfo);
-    }
-
     if (flags_ & SVC_EXEC) UnSetExec();
 
     if (flags_ & SVC_TEMPORARY) return;
@@ -445,8 +446,8 @@
     const std::string& interface_name = args[1];
     const std::string& instance_name = args[2];
 
-    const FQName fq_name = FQName(interface_name);
-    if (!fq_name.isValid()) {
+    FQName fq_name;
+    if (!FQName::parse(interface_name, &fq_name)) {
         return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
     }
 
@@ -677,29 +678,29 @@
         {"console",     {0,     1,    &Service::ParseConsole}},
         {"critical",    {0,     0,    &Service::ParseCritical}},
         {"disabled",    {0,     0,    &Service::ParseDisabled}},
+        {"file",        {2,     2,    &Service::ParseFile}},
         {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
         {"interface",   {2,     2,    &Service::ParseInterface}},
         {"ioprio",      {2,     2,    &Service::ParseIoprio}},
-        {"priority",    {1,     1,    &Service::ParsePriority}},
         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
-        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
-        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
-        {"override",    {0,     0,    &Service::ParseOverride}},
-        {"oom_score_adjust",
-                        {1,     1,    &Service::ParseOomScoreAdjust}},
-        {"memcg.swappiness",
-                        {1,     1,    &Service::ParseMemcgSwappiness}},
-        {"memcg.soft_limit_in_bytes",
-                        {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
         {"memcg.limit_in_bytes",
                         {1,     1,    &Service::ParseMemcgLimitInBytes}},
+        {"memcg.soft_limit_in_bytes",
+                        {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
+        {"memcg.swappiness",
+                        {1,     1,    &Service::ParseMemcgSwappiness}},
         {"namespace",   {1,     2,    &Service::ParseNamespace}},
+        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
+        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
+        {"oom_score_adjust",
+                        {1,     1,    &Service::ParseOomScoreAdjust}},
+        {"override",    {0,     0,    &Service::ParseOverride}},
+        {"priority",    {1,     1,    &Service::ParsePriority}},
         {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
         {"shutdown",    {1,     1,    &Service::ParseShutdown}},
         {"socket",      {3,     6,    &Service::ParseSocket}},
-        {"file",        {2,     2,    &Service::ParseFile}},
         {"user",        {1,     1,    &Service::ParseUser}},
         {"writepid",    {1,     kMax, &Service::ParseWritepid}},
     };
@@ -1168,7 +1169,7 @@
     // Property values can contain any characters, but may only be a certain length.
     // (The latter restriction is needed because `start` and `stop` work by writing
     // the service name to the "ctl.start" and "ctl.stop" properties.)
-    return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+    return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
 }
 
 }  // namespace init
diff --git a/init/service.h b/init/service.h
index bcf1943..d46a413 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,7 +17,6 @@
 #ifndef _INIT_SERVICE_H
 #define _INIT_SERVICE_H
 
-#include <signal.h>
 #include <sys/resource.h>
 #include <sys/types.h>
 
@@ -82,7 +81,7 @@
     void Stop();
     void Terminate();
     void Restart();
-    void Reap(const siginfo_t& siginfo);
+    void Reap();
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
     bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
@@ -90,9 +89,6 @@
         is_exec_service_running_ = false;
         flags_ &= ~SVC_EXEC;
     }
-    void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
-        reap_callbacks_.emplace_back(std::move(callback));
-    }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
 
@@ -214,8 +210,6 @@
     std::vector<std::pair<int, rlimit>> rlimits_;
 
     std::vector<std::string> args_;
-
-    std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
 };
 
 class ServiceList {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index badacaf..072a0fb 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -84,15 +84,16 @@
         }
     }
 
-    if (siginfo.si_code == CLD_EXITED) {
-        LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
-    } else {
-        LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
+    auto status = siginfo.si_status;
+    if (WIFEXITED(status)) {
+        LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
+    } else if (WIFSIGNALED(status)) {
+        LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
     }
 
     if (!service) return true;
 
-    service->Reap(siginfo);
+    service->Reap();
 
     if (service->flags() & SVC_TEMPORARY) {
         ServiceList::GetInstance().RemoveService(*service);
diff --git a/init/stable_properties.h b/init/stable_properties.h
index be35457..c8bdaa4 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -29,6 +29,7 @@
 };
 
 static const std::set<std::string> kExportedActionableProperties = {
+    "init.svc.console",
     "init.svc.mediadrm",
     "init.svc.zygote",
     "persist.bluetooth.btsnoopenable",
@@ -36,6 +37,7 @@
     "persist.sys.zram_enabled",
     "ro.bootmode",
     "ro.build.type",
+    "ro.debuggable",
     "sys.boot_completed",
     "sys.retaildemo.enabled",
     "sys.shutdown.requested",
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3b643a..c1846f7 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -27,12 +27,14 @@
 #include <selinux/android.h>
 
 #include "action.h"
-#include "property_service.h"
-#include "selinux.h"
 #include "util.h"
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
+#if defined(__ANDROID__)
+#include "property_service.h"
+#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
 
 using android::base::GetExecutablePath;
 using android::base::Join;
@@ -47,6 +49,11 @@
 const std::string kInitContext = "u:r:init:s0";
 const std::string kVendorContext = "u:r:vendor_init:s0";
 
+const char* const paths_and_secontexts[2][2] = {
+    {"/vendor", kVendorContext.c_str()},
+    {"/odm", kVendorContext.c_str()},
+};
+
 namespace {
 
 constexpr size_t kBufferSize = 4096;
@@ -83,7 +90,7 @@
 
 uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
     properties_to_set.emplace_back(name, value);
-    return PROP_SUCCESS;
+    return 0;
 }
 
 class SubcontextProcess {
@@ -295,7 +302,11 @@
 
     for (const auto& property : subcontext_reply->properties_to_set()) {
         ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
-        HandlePropertySet(property.name(), property.value(), context_, cr);
+        std::string error;
+        if (HandlePropertySet(property.name(), property.value(), context_, cr, &error) != 0) {
+            LOG(ERROR) << "Subcontext init could not set '" << property.name() << "' to '"
+                       << property.value() << "': " << error;
+        }
     }
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
@@ -343,9 +354,6 @@
 static std::vector<Subcontext> subcontexts;
 
 std::vector<Subcontext>* InitializeSubcontexts() {
-    static const char* const paths_and_secontexts[][2] = {
-        {"/vendor", kVendorContext.c_str()},
-    };
     for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
         subcontexts.emplace_back(path_prefix, secontext);
     }
diff --git a/init/subcontext.h b/init/subcontext.h
index 5601b80..22d7d43 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -33,6 +33,7 @@
 
 extern const std::string kInitContext;
 extern const std::string kVendorContext;
+extern const char* const paths_and_secontexts[2][2];
 
 class Subcontext {
   public:
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index ac1d7c7..24b14c4 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -83,8 +83,8 @@
 }
 
 UeventListener::UeventListener() {
-    // is 256K enough? udev uses 16MB!
-    device_fd_.reset(uevent_open_socket(256 * 1024, true));
+    // is 2MB enough? udev uses 128MB!
+    device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
     if (device_fd_ == -1) {
         LOG(FATAL) << "Could not open uevent socket";
     }
diff --git a/init/util.cpp b/init/util.cpp
index d80cb1e..4455b2e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,7 +33,6 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -42,7 +41,14 @@
 #include <selinux/android.h>
 
 #include "reboot.h"
+
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+
 #include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
 
 #ifdef _INIT_INIT_H
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -409,5 +415,30 @@
     return false;
 }
 
+bool IsLegalPropertyName(const std::string& name) {
+    size_t namelen = name.size();
+
+    if (namelen < 1) return false;
+    if (name[0] == '.') return false;
+    if (name[namelen - 1] == '.') return false;
+
+    /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
+    /* Don't allow ".." to appear in a property name */
+    for (size_t i = 0; i < namelen; i++) {
+        if (name[i] == '.') {
+            // i=0 is guaranteed to never have a dot. See above.
+            if (name[i - 1] == '.') return false;
+            continue;
+        }
+        if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
+        if (name[i] >= 'a' && name[i] <= 'z') continue;
+        if (name[i] >= 'A' && name[i] <= 'Z') continue;
+        if (name[i] >= '0' && name[i] <= '9') continue;
+        return false;
+    }
+
+    return true;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 2cfcf6c..07e4864 100644
--- a/init/util.h
+++ b/init/util.h
@@ -62,6 +62,8 @@
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
 
+bool IsLegalPropertyName(const std::string& name);
+
 }  // namespace init
 }  // namespace android
 
diff --git a/libasyncio/AsyncIO.cpp b/libasyncio/AsyncIO.cpp
index 7430bc8..6149f09 100644
--- a/libasyncio/AsyncIO.cpp
+++ b/libasyncio/AsyncIO.cpp
@@ -17,9 +17,10 @@
 #include <asyncio/AsyncIO.h>
 #include <sys/syscall.h>
 #include <unistd.h>
+#include <cstdint>
+#include <cstring>
 
 int io_setup(unsigned nr, aio_context_t* ctxp) {
-    memset(ctxp, 0, sizeof(*ctxp));
     return syscall(__NR_io_setup, nr, ctxp);
 }
 
@@ -48,3 +49,11 @@
     iocb->aio_nbytes = count;
     iocb->aio_offset = offset;
 }
+
+void io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {
+    io_prep(iocb, fd, buf, count, offset, true);
+}
+
+void io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {
+    io_prep(iocb, fd, buf, count, offset, false);
+}
diff --git a/libasyncio/include/asyncio/AsyncIO.h b/libasyncio/include/asyncio/AsyncIO.h
index e3fb93a..9620d2a 100644
--- a/libasyncio/include/asyncio/AsyncIO.h
+++ b/libasyncio/include/asyncio/AsyncIO.h
@@ -17,9 +17,9 @@
 #ifndef _ASYNCIO_H
 #define _ASYNCIO_H
 
-#include <cstring>
-#include <cstdint>
 #include <linux/aio_abi.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <time.h>
@@ -35,10 +35,14 @@
 
 int io_setup(unsigned nr, aio_context_t* ctxp);
 int io_destroy(aio_context_t ctx);
-int io_submit(aio_context_t ctx, long nr, iocb** iocbpp);
-int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout);
-int io_cancel(aio_context_t ctx, iocb*, io_event* result);
-void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
+int io_submit(aio_context_t ctx, long nr, struct iocb** iocbpp);
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, struct io_event* events,
+                 struct timespec* timeout);
+int io_cancel(aio_context_t ctx, struct iocb*, struct io_event* result);
+
+void io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);
+void io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);
+void io_prep(struct iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
 
 #ifdef __cplusplus
 };
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 0e32e47..e087b2e 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -128,6 +128,22 @@
   return true;
 }
 
+bool Backtrace::UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+                              const backtrace_stackinfo_t& stack,
+                              std::vector<backtrace_frame_data_t>* frames,
+                              BacktraceUnwindError* error) {
+  UnwindStackOfflineMap* offline_map = reinterpret_cast<UnwindStackOfflineMap*>(back_map);
+  // Create the process memory from the stack data since this will almost
+  // always be different each unwind.
+  if (!offline_map->CreateProcessMemory(stack)) {
+    if (error != nullptr) {
+      error->error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    }
+    return false;
+  }
+  return Backtrace::Unwind(regs, back_map, frames, 0U, nullptr, error);
+}
+
 UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
     : BacktraceCurrent(pid, tid, map) {}
 
@@ -147,6 +163,9 @@
   }
 
   std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
+  if (!skip_frames_) {
+    skip_names.clear();
+  }
   return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names, &error_);
 }
 
@@ -221,12 +240,12 @@
 Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
                                     const std::vector<backtrace_map_t>& maps,
                                     const backtrace_stackinfo_t& stack) {
-  BacktraceMap* map = BacktraceMap::CreateOffline(pid, maps, stack);
-  if (map == nullptr) {
+  std::unique_ptr<UnwindStackOfflineMap> map(
+      reinterpret_cast<UnwindStackOfflineMap*>(BacktraceMap::CreateOffline(pid, maps)));
+  if (map.get() == nullptr || !map->CreateProcessMemory(stack)) {
     return nullptr;
   }
-
-  return new UnwindStackOffline(arch, pid, tid, map, false);
+  return new UnwindStackOffline(arch, pid, tid, map.release(), false);
 }
 
 Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 6dcc621..9c6fed4 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -127,12 +127,7 @@
   return false;
 }
 
-bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps,
-                                  const backtrace_stackinfo_t& stack) {
-  if (stack.start >= stack.end) {
-    return false;
-  }
-
+bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps) {
   for (const backtrace_map_t& map : backtrace_maps) {
     maps_.push_back(map);
   }
@@ -145,6 +140,13 @@
   for (const backtrace_map_t& map : maps_) {
     maps->Add(map.start, map.end, map.offset, map.flags, map.name, map.load_bias);
   }
+  return true;
+}
+
+bool UnwindStackOfflineMap::CreateProcessMemory(const backtrace_stackinfo_t& stack) {
+  if (stack.start >= stack.end) {
+    return false;
+  }
 
   // Create the process memory from the stack data.
   uint64_t size = stack.end - stack.start;
@@ -154,7 +156,6 @@
   std::shared_ptr<unwindstack::Memory> shared_memory(memory);
 
   process_memory_.reset(new unwindstack::MemoryRange(shared_memory, 0, size, stack.start));
-
   return true;
 }
 
@@ -182,10 +183,9 @@
 //-------------------------------------------------------------------------
 // BacktraceMap create offline function.
 //-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
-                                          const backtrace_stackinfo_t& stack) {
+BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps) {
   UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
-  if (!map->Build(maps, stack)) {
+  if (!map->Build(maps)) {
     delete map;
     return nullptr;
   }
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 94cbfb2..ec0d9c1 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -76,7 +76,9 @@
 
   bool Build() override;
 
-  bool Build(const std::vector<backtrace_map_t>& maps, const backtrace_stackinfo_t& stack);
+  bool Build(const std::vector<backtrace_map_t>& maps);
+
+  bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
 };
 
 #endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index aab6db9..1e3d379 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -69,6 +69,9 @@
 // Number of simultaneous threads running in our forked process.
 #define NUM_PTRACE_THREADS 5
 
+// The list of shared libaries that make up the backtrace library.
+static std::vector<std::string> kBacktraceLibs{"libunwindstack.so", "libbacktrace.so"};
+
 struct thread_t {
   pid_t tid;
   int32_t state;
@@ -256,16 +259,49 @@
   VERIFY_NO_ERROR(backtrace->GetError().error_code);
 
   ASSERT_TRUE(backtrace->NumFrames() != 0);
+  // None of the frames should be in the backtrace libraries.
   for (const auto& frame : *backtrace ) {
     if (BacktraceMap::IsValid(frame.map)) {
       const std::string name = basename(frame.map.name.c_str());
-      ASSERT_TRUE(name != "libunwind.so" && name != "libbacktrace.so")
-        << DumpFrames(backtrace.get());
+      for (const auto& lib : kBacktraceLibs) {
+        ASSERT_TRUE(name != lib) << DumpFrames(backtrace.get());
+      }
     }
-    break;
   }
 }
 
+TEST(libbacktrace, local_unwind_frames) {
+  // Verify that a local unwind with the skip frames disabled does include
+  // frames within the backtrace libraries.
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
+  ASSERT_TRUE(backtrace.get() != nullptr);
+  backtrace->SetSkipFrames(false);
+  ASSERT_TRUE(backtrace->Unwind(0));
+  VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+  ASSERT_TRUE(backtrace->NumFrames() != 0);
+  size_t first_frame_non_backtrace_lib = 0;
+  for (const auto& frame : *backtrace) {
+    if (BacktraceMap::IsValid(frame.map)) {
+      const std::string name = basename(frame.map.name.c_str());
+      bool found = false;
+      for (const auto& lib : kBacktraceLibs) {
+        if (name == lib) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        first_frame_non_backtrace_lib = frame.num;
+        break;
+      }
+    }
+  }
+
+  ASSERT_NE(0U, first_frame_non_backtrace_lib) << "No frames found in backtrace libraries:\n"
+                                               << DumpFrames(backtrace.get());
+}
+
 TEST(libbacktrace, local_trace) {
   ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
 }
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 7a37015..735a2f3 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -151,6 +151,11 @@
                      std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
                      std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
 
+  static bool UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+                            const backtrace_stackinfo_t& stack_info,
+                            std::vector<backtrace_frame_data_t>* frames,
+                            BacktraceUnwindError* error = nullptr);
+
   // Get the function name and offset into the function given the pc.
   // If the string is empty, then no valid function name was found,
   // or the pc is not in any valid map.
@@ -199,6 +204,9 @@
 
   std::string GetErrorString(BacktraceUnwindError error);
 
+  // Set whether to skip frames in libbacktrace/libunwindstack when doing a local unwind.
+  void SetSkipFrames(bool skip_frames) { skip_frames_ = skip_frames; }
+
  protected:
   Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
 
@@ -218,6 +226,9 @@
 
   std::vector<backtrace_frame_data_t> frames_;
 
+  // Skip frames in libbacktrace/libunwindstack when doing a local unwind.
+  bool skip_frames_ = true;
+
   BacktraceUnwindError error_;
 };
 
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index da54472..c94cad1 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -40,6 +40,10 @@
 // Special flag to indicate a map is in /dev/. However, a map in
 // /dev/ashmem/... does not set this flag.
 static constexpr int PROT_DEVICE_MAP = 0x8000;
+// Special flag to indicate that this map represents an elf file
+// created by ART for use with the gdb jit debug interface.
+// This should only ever appear in offline maps data.
+static constexpr int PROT_JIT_SYMFILE_MAP = 0x4000;
 
 struct backtrace_map_t {
   uint64_t start = 0;
@@ -64,8 +68,7 @@
   // is unsupported.
   static BacktraceMap* Create(pid_t pid, bool uncached = false);
 
-  static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
-                                     const backtrace_stackinfo_t& stack);
+  static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps);
 
   virtual ~BacktraceMap();
 
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 3abf958..f6f7128 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -203,16 +203,6 @@
                                            CAP_MASK_LONG(CAP_SYS_MODULE),
                                            "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
-    // A non-privileged zygote that spawns isolated processes for web rendering.
-    { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
-                                           CAP_MASK_LONG(CAP_SETGID) |
-                                           CAP_MASK_LONG(CAP_SETPCAP),
-                                              "system/bin/webview_zygote32" },
-    { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
-                                           CAP_MASK_LONG(CAP_SETGID) |
-                                           CAP_MASK_LONG(CAP_SETPCAP),
-                                              "system/bin/webview_zygote64" },
-
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index b2779b2..bbb150d 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -73,7 +73,8 @@
 #define ATRACE_TAG_NETWORK          (1<<21)
 #define ATRACE_TAG_ADB              (1<<22)
 #define ATRACE_TAG_VIBRATOR         (1<<23)
-#define ATRACE_TAG_LAST             ATRACE_TAG_VIBRATOR
+#define ATRACE_TAG_AIDL             (1<<24)
+#define ATRACE_TAG_LAST             ATRACE_TAG_AIDL
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/libion/ion.c b/libion/ion.c
index 5836128..b8de5a4 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -55,7 +55,7 @@
 
 int ion_open() {
     int fd = open("/dev/ion", O_RDONLY | O_CLOEXEC);
-    if (fd < 0) ALOGE("open /dev/ion failed!\n");
+    if (fd < 0) ALOGE("open /dev/ion failed: %s", strerror(errno));
 
     return fd;
 }
@@ -69,7 +69,7 @@
 static int ion_ioctl(int fd, int req, void* arg) {
     int ret = ioctl(fd, req, arg);
     if (ret < 0) {
-        ALOGE("ioctl %x failed with code %d: %s\n", req, ret, strerror(errno));
+        ALOGE("ioctl %x failed with code %d: %s", req, ret, strerror(errno));
         return -errno;
     }
     return ret;
@@ -115,12 +115,12 @@
     ret = ion_ioctl(fd, ION_IOC_MAP, &data);
     if (ret < 0) return ret;
     if (data.fd < 0) {
-        ALOGE("map ioctl returned negative fd\n");
+        ALOGE("map ioctl returned negative fd");
         return -EINVAL;
     }
     tmp_ptr = mmap(NULL, length, prot, flags, data.fd, offset);
     if (tmp_ptr == MAP_FAILED) {
-        ALOGE("mmap failed: %s\n", strerror(errno));
+        ALOGE("mmap failed: %s", strerror(errno));
         return -errno;
     }
     *map_fd = data.fd;
@@ -140,7 +140,7 @@
     ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
     if (ret < 0) return ret;
     if (data.fd < 0) {
-        ALOGE("share ioctl returned negative fd\n");
+        ALOGE("share ioctl returned negative fd");
         return -EINVAL;
     }
     *share_fd = data.fd;
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index d03a2b6..2754e6e 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -243,7 +243,7 @@
 
 static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
   struct android_log_transport_write* node;
-  int ret;
+  int ret, save_errno;
   struct timespec ts;
   size_t len, i;
 
@@ -254,20 +254,24 @@
     return -EINVAL;
   }
 
+  save_errno = errno;
 #if defined(__ANDROID__)
   clock_gettime(android_log_clockid(), &ts);
 
   if (log_id == LOG_ID_SECURITY) {
     if (vec[0].iov_len < 4) {
+      errno = save_errno;
       return -EINVAL;
     }
 
     ret = check_log_uid_permissions();
     if (ret < 0) {
+      errno = save_errno;
       return ret;
     }
     if (!__android_log_security()) {
       /* If only we could reset downstream logd counter */
+      errno = save_errno;
       return -EPERM;
     }
   } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
@@ -276,6 +280,7 @@
     EventTagMap *m, *f;
 
     if (vec[0].iov_len < 4) {
+      errno = save_errno;
       return -EINVAL;
     }
 
@@ -311,6 +316,7 @@
       android_closeEventTagMap(f);
     }
     if (!ret) {
+      errno = save_errno;
       return -EPERM;
     }
   } else {
@@ -340,6 +346,7 @@
     }
 
     if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
+      errno = save_errno;
       return -EPERM;
     }
   }
@@ -371,21 +378,23 @@
     }
   }
 
+  errno = save_errno;
   return ret;
 }
 
 static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {
+  int ret, save_errno = errno;
+
   __android_log_lock();
 
   if (write_to_log == __write_to_log_init) {
-    int ret;
-
     ret = __write_to_log_initialize();
     if (ret < 0) {
       __android_log_unlock();
       if (!list_empty(&__android_log_persist_write)) {
         __write_to_log_daemon(log_id, vec, nr);
       }
+      errno = save_errno;
       return ret;
     }
 
@@ -394,7 +403,9 @@
 
   __android_log_unlock();
 
-  return write_to_log(log_id, vec, nr);
+  ret = write_to_log(log_id, vec, nr);
+  errno = save_errno;
+  return ret;
 }
 
 LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag,
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index b428dd7..f872d0f 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -20,7 +20,7 @@
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libmemunreachable",
     defaults: ["libmemunreachable_defaults"],
     srcs: [
@@ -90,12 +90,11 @@
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "tests/Binder_test.cpp",
-        "tests/MemUnreachable_test.cpp",
     ],
+    static_libs: ["libmemunreachable"],
     shared_libs: [
         "libbinder",
         "libhwbinder",
-        "libmemunreachable",
         "libutils",
     ],
 }
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
index 6e85d5a..eaf7652 100644
--- a/libmemunreachable/tests/Binder_test.cpp
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -33,6 +33,9 @@
 
 static const String16 service_name("test.libmemunreachable_binder");
 
+// Provides a service that will hold a strong reference to any remote binder
+// object, so that the test can verify that a remote strong reference is
+// visible to libmemunreachable.
 class BinderService : public BBinder {
  public:
   BinderService() = default;
@@ -55,6 +58,8 @@
   ~BinderObject() = default;
 };
 
+// Forks a subprocess that registers a BinderService with the global binder
+// servicemanager.  Requires root permissions.
 class ServiceProcess {
  public:
   ServiceProcess() : child_(0) {}
@@ -97,6 +102,7 @@
       fprintf(stderr, "Failed to get service manager\n");
       return 1;
     }
+    // This step requires root permissions
     if (sm->addService(service_name, new BinderService()) != OK) {
       fprintf(stderr, "Failed to add test service\n");
       return 1;
@@ -110,12 +116,18 @@
   pid_t child_;
 };
 
-class BinderTest : public ::testing::Test {
+class MemunreachableBinderTest : public ::testing::Test {
  protected:
   ServiceProcess service_process_;
 };
 
-TEST_F(BinderTest, binder) {
+// Tests that a local binder object with a remote strong reference is visible
+// through the libmemunreachable BinderReferences interface, which uses the
+// getBinderKernelReferences method in libbinder.  Starts a BinderService
+// through ServiceProcess as a remote service to hold the strong reference.
+TEST_F(MemunreachableBinderTest, binder) {
+  ASSERT_EQ(static_cast<uid_t>(0), getuid()) << "This test must be run as root.";
+
   ServiceProcess service_process;
   ASSERT_TRUE(service_process.Run());
 
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 1974f2c..b0bc497 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,7 +1,7 @@
 cc_library {
     srcs: ["processgroup.cpp"],
     name: "libprocessgroup",
-    defaults: ["linux_bionic_supported"],
+    host_supported: true,
     shared_libs: ["libbase"],
     export_include_dirs: ["include"],
     cflags: [
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8526b3a..6dfa697 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -33,15 +34,19 @@
 #include <memory>
 #include <mutex>
 #include <set>
+#include <string>
 #include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/unique_fd.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
+using android::base::StartsWith;
+using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
 using namespace std::chrono_literals;
@@ -50,171 +55,58 @@
 #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
 #define ACCT_CGROUP_PATH "/acct"
 
-#define PROCESSGROUP_UID_PREFIX "uid_"
-#define PROCESSGROUP_PID_PREFIX "pid_"
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-#define PROCESSGROUP_MAX_UID_LEN 11
-#define PROCESSGROUP_MAX_PID_LEN 11
-#define PROCESSGROUP_MAX_PATH_LEN \
-        ((sizeof(MEM_CGROUP_PATH) > sizeof(ACCT_CGROUP_PATH) ? \
-          sizeof(MEM_CGROUP_PATH) : sizeof(ACCT_CGROUP_PATH)) + \
-         sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
-         PROCESSGROUP_MAX_UID_LEN + \
-         sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
-         PROCESSGROUP_MAX_PID_LEN + \
-         sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
-         1)
 
 std::once_flag init_path_flag;
 
-class ProcessGroup {
-  public:
-    ProcessGroup() : buf_ptr_(buf_), buf_len_(0) {}
-
-    bool Open(uid_t uid, int pid);
-
-    // Return positive number and sets *pid = next pid in process cgroup on success
-    // Returns 0 if there are no pids left in the process cgroup
-    // Returns -errno if an error was encountered
-    int GetOneAppProcess(pid_t* pid);
-
-  private:
-    // Returns positive number of bytes filled on success
-    // Returns 0 if there was nothing to read
-    // Returns -errno if an error was encountered
-    int RefillBuffer();
-
-    android::base::unique_fd fd_;
-    char buf_[128];
-    char* buf_ptr_;
-    size_t buf_len_;
-};
-
-static const char* getCgroupRootPath() {
-    static const char* cgroup_root_path = NULL;
+static const std::string& GetCgroupRootPath() {
+    static std::string cgroup_root_path;
     std::call_once(init_path_flag, [&]() {
             // Check if mem cgroup is mounted, only then check for write-access to avoid
             // SELinux denials
-            cgroup_root_path = access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
-                    ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
+            cgroup_root_path =
+                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
+                                                                                 : MEM_CGROUP_PATH);
             });
     return cgroup_root_path;
 }
 
-static int convertUidToPath(char *path, size_t size, uid_t uid)
-{
-    return snprintf(path, size, "%s/%s%d",
-            getCgroupRootPath(),
-            PROCESSGROUP_UID_PREFIX,
-            uid);
+static std::string ConvertUidToPath(uid_t uid) {
+    return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
 }
 
-static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
-{
-    return snprintf(path, size, "%s/%s%d/%s%d",
-            getCgroupRootPath(),
-            PROCESSGROUP_UID_PREFIX,
-            uid,
-            PROCESSGROUP_PID_PREFIX,
-            pid);
+static std::string ConvertUidPidToPath(uid_t uid, int pid) {
+    return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
 }
 
-bool ProcessGroup::Open(uid_t uid, int pid) {
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
-    convertUidPidToPath(path, sizeof(path), uid, pid);
-    strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
-
-    int fd = open(path, O_RDONLY);
-    if (fd < 0) return false;
-
-    fd_.reset(fd);
-
-    LOG(VERBOSE) << "Initialized context for " << path;
-
-    return true;
-}
-
-int ProcessGroup::RefillBuffer() {
-    memmove(buf_, buf_ptr_, buf_len_);
-    buf_ptr_ = buf_;
-
-    ssize_t ret = read(fd_, buf_ptr_ + buf_len_, sizeof(buf_) - buf_len_ - 1);
-    if (ret < 0) {
-        return -errno;
-    } else if (ret == 0) {
-        return 0;
-    }
-
-    buf_len_ += ret;
-    buf_[buf_len_] = 0;
-    LOG(VERBOSE) << "Read " << ret << " to buffer: " << buf_;
-
-    assert(buf_len_ <= sizeof(buf_));
-
-    return ret;
-}
-
-int ProcessGroup::GetOneAppProcess(pid_t* out_pid) {
-    *out_pid = 0;
-
-    char* eptr;
-    while ((eptr = static_cast<char*>(memchr(buf_ptr_, '\n', buf_len_))) == nullptr) {
-        int ret = RefillBuffer();
-        if (ret <= 0) return ret;
-    }
-
-    *eptr = '\0';
-    char* pid_eptr = nullptr;
-    errno = 0;
-    long pid = strtol(buf_ptr_, &pid_eptr, 10);
-    if (errno != 0) {
-        return -errno;
-    }
-    if (pid_eptr != eptr) {
-        errno = EINVAL;
-        return -errno;
-    }
-
-    buf_len_ -= (eptr - buf_ptr_) + 1;
-    buf_ptr_ = eptr + 1;
-
-    *out_pid = static_cast<pid_t>(pid);
-    return 1;
-}
-
-static int removeProcessGroup(uid_t uid, int pid)
-{
+static int RemoveProcessGroup(uid_t uid, int pid) {
     int ret;
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
 
-    convertUidPidToPath(path, sizeof(path), uid, pid);
-    ret = rmdir(path);
+    auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+    ret = rmdir(uid_pid_path.c_str());
 
-    convertUidToPath(path, sizeof(path), uid);
-    rmdir(path);
+    auto uid_path = ConvertUidToPath(uid);
+    rmdir(uid_path.c_str());
 
     return ret;
 }
 
-static void removeUidProcessGroups(const char *uid_path)
-{
-    std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
+static void RemoveUidProcessGroups(const std::string& uid_path) {
+    std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
     if (uid != NULL) {
         dirent* dir;
         while ((dir = readdir(uid.get())) != nullptr) {
-            char path[PROCESSGROUP_MAX_PATH_LEN];
-
             if (dir->d_type != DT_DIR) {
                 continue;
             }
 
-            if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
+            if (!StartsWith(dir->d_name, "pid_")) {
                 continue;
             }
 
-            snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
+            auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
             LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
@@ -222,38 +114,38 @@
 void removeAllProcessGroups()
 {
     LOG(VERBOSE) << "removeAllProcessGroups()";
-    const char* cgroup_root_path = getCgroupRootPath();
-    std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
+    const auto& cgroup_root_path = GetCgroupRootPath();
+    std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
     if (root == NULL) {
         PLOG(ERROR) << "Failed to open " << cgroup_root_path;
     } else {
         dirent* dir;
         while ((dir = readdir(root.get())) != nullptr) {
-            char path[PROCESSGROUP_MAX_PATH_LEN];
-
             if (dir->d_type != DT_DIR) {
                 continue;
             }
-            if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
+
+            if (!StartsWith(dir->d_name, "uid_")) {
                 continue;
             }
 
-            snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
-            removeUidProcessGroups(path);
+            auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
+            RemoveUidProcessGroups(path);
             LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
 
 // Returns number of processes killed on success
 // Returns 0 if there are no processes in the process cgroup left to kill
-// Returns -errno on error
-static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    ProcessGroup process_group;
-    if (!process_group.Open(uid, initialPid)) {
+// Returns -1 on error
+static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+    auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+    std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
+    if (!fd) {
         PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
-        return -errno;
+        return -1;
     }
 
     // We separate all of the pids in the cgroup into those pids that are also the leaders of
@@ -262,10 +154,9 @@
     pgids.emplace(initialPid);
     std::set<pid_t> pids;
 
-    int ret;
     pid_t pid;
     int processes = 0;
-    while ((ret = process_group.GetOneAppProcess(&pid)) > 0 && pid >= 0) {
+    while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
         processes++;
         if (pid == 0) {
             // Should never happen...  but if it does, trying to kill this
@@ -312,15 +203,15 @@
         }
     }
 
-    return ret >= 0 ? processes : ret;
+    return feof(fd.get()) ? processes : -1;
 }
 
-static int killProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
     int retry = retries;
     int processes;
-    while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
+    while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
         LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
@@ -350,7 +241,7 @@
             LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
                       << " in " << static_cast<int>(ms) << "ms";
         }
-        return removeProcessGroup(uid, initialPid);
+        return RemoveProcessGroup(uid, initialPid);
     } else {
         if (retries > 0) {
             LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -362,22 +253,21 @@
 }
 
 int killProcessGroup(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 40 /*retries*/);
+    return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
 }
 
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 0 /*retries*/);
+    return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
 }
 
-static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
-{
-    if (mkdir(path, mode) == -1 && errno != EEXIST) {
+static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+    if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
         return false;
     }
 
-    if (chown(path, uid, gid) == -1) {
+    if (chown(path.c_str(), uid, gid) == -1) {
         int saved_errno = errno;
-        rmdir(path);
+        rmdir(path.c_str());
         errno = saved_errno;
         return false;
     }
@@ -387,42 +277,38 @@
 
 int createProcessGroup(uid_t uid, int initialPid)
 {
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+    auto uid_path = ConvertUidToPath(uid);
 
-    convertUidToPath(path, sizeof(path), uid);
-
-    if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "Failed to make and chown " << path;
+    if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+        PLOG(ERROR) << "Failed to make and chown " << uid_path;
         return -errno;
     }
 
-    convertUidPidToPath(path, sizeof(path), uid, initialPid);
+    auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
 
-    if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "Failed to make and chown " << path;
+    if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+        PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
         return -errno;
     }
 
-    strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
+    auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
 
     int ret = 0;
-    if (!WriteStringToFile(std::to_string(initialPid), path)) {
+    if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
         ret = -errno;
-        PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path;
+        PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
     }
 
     return ret;
 }
 
-static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) {
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
-    if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) {
-        PLOG(ERROR) << "Memcg is not mounted." << path;
+static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
+    if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+        PLOG(ERROR) << "Memcg is not mounted.";
         return false;
     }
 
-    convertUidPidToPath(path, sizeof(path), uid, pid);
-    strlcat(path, fileName, sizeof(path));
+    auto path = ConvertUidPidToPath(uid, pid) + file_name;
 
     if (!WriteStringToFile(std::to_string(value), path)) {
         PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
@@ -432,13 +318,13 @@
 }
 
 bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
-    return setProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+    return SetProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
 }
 
 bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
-    return setProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+    return SetProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
 }
 
 bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
-    return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+    return SetProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
 }
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 7f92904..35a3063 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -137,6 +137,12 @@
         switch(rta->rta_type) {
             case IFLA_IFNAME:
                 asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta));
+                // We can get the interface change information from sysfs update
+                // already. But in case we missed those message when devices start.
+                // We do a update again when received a kLinkUp event. To make
+                // the message consistent, use IFINDEX here as well since sysfs
+                // uses IFINDEX.
+                asprintf(&mParams[1], "IFINDEX=%d", ifi->ifi_index);
                 mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? Action::kLinkUp :
                                                             Action::kLinkDown;
                 mSubsystem = strdup("net");
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 124c70e..0d43f87 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,6 +47,8 @@
 
     srcs: [
         "ArmExidx.cpp",
+        "DexFile.cpp",
+        "DexFiles.cpp",
         "DwarfCfa.cpp",
         "DwarfEhFrameWithHdr.cpp",
         "DwarfMemory.cpp",
@@ -85,11 +87,13 @@
         },
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_static_libs: ["libunwindstack_dex"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
             exclude_shared_libs: ["libdexfile"],
         },
     },
-    whole_static_libs: ["libunwindstack_dex"],
 
     arch: {
         x86: {
@@ -114,79 +118,9 @@
     ],
 }
 
-// Isolate the dex file processing into a separate library. Currently,
-// it is necessary to add art include directories directly, which also
-// adds the art elf.h file in the include path, overriding the system one.
-// Work to isolate libdexfile is b/72216369.
-cc_library_static {
-    name: "libunwindstack_dex",
-    vendor_available: false,
-    defaults: ["libunwindstack_flags"],
-
-    cflags: [
-        "-Wexit-time-destructors",
-    ],
-
-    srcs: [
-        "DexFile.cpp",
-        "DexFiles.cpp",
-    ],
-    target: {
-        // Always disable optimizations for host to make it easier to debug.
-        host: {
-            cflags: [
-                "-O0",
-                "-g",
-            ],
-        },
-    },
-
-    shared_libs: [
-        "libbase",
-        "libdexfile",
-    ],
-    local_include_dirs: ["include"],
-    allow_undefined_symbols: true,
-
-    // libdexfile will eventually properly export headers, for now include
-    // these directly.
-    include_dirs: [
-        "art/runtime",
-    ],
-}
-
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
-cc_test_library {
-    name: "libunwindstack_dex_test",
-    vendor_available: false,
-    defaults: ["libunwindstack_flags"],
-
-    shared: {
-        enabled: false,
-    },
-
-    srcs: [
-        "tests/DexFileTest.cpp",
-        "tests/DexFilesTest.cpp",
-    ],
-    local_include_dirs: ["include"],
-    allow_undefined_symbols: true,
-
-    shared_libs: [
-        "libbase",
-        "libunwindstack",
-        "libdexfile",
-    ],
-
-    // libdexfile will eventually properly export headers, for now include
-    // these directly.
-    include_dirs: [
-        "art/runtime",
-    ],
-}
-
 cc_test {
     name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
@@ -194,6 +128,8 @@
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
+        "tests/DexFileTest.cpp",
+        "tests/DexFilesTest.cpp",
         "tests/DwarfCfaLogTest.cpp",
         "tests/DwarfCfaTest.cpp",
         "tests/DwarfDebugFrameTest.cpp",
@@ -242,22 +178,23 @@
         "liblog",
         "liblzma",
         "libunwindstack",
+        "libdexfile",
     ],
 
     static_libs: [
         "libgmock",
     ],
 
-    whole_static_libs: ["libunwindstack_dex_test"],
-
     data: [
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
+        "tests/files/offline/art_quick_osr_stub_arm/*",
         "tests/files/offline/bad_eh_frame_hdr_arm64/*",
         "tests/files/offline/debug_frame_first_x86/*",
         "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
         "tests/files/offline/jit_debug_arm/*",
         "tests/files/offline/jit_debug_x86/*",
+        "tests/files/offline/jit_map_arm/*",
         "tests/files/offline/gnu_debugdata_arm/*",
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index c5f8138..430f6c5 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -77,7 +77,8 @@
 
 uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
   uint32_t entry;
-  if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
+  const uint32_t field_offset = 12;  // offset of first_entry_ in the descriptor struct.
+  if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
     return 0;
   }
   return entry;
@@ -85,7 +86,8 @@
 
 uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
   uint64_t entry;
-  if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
+  const uint32_t field_offset = 16;  // offset of first_entry_ in the descriptor struct.
+  if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
     return 0;
   }
   return entry;
@@ -122,7 +124,7 @@
   initialized_ = true;
   entry_addr_ = 0;
 
-  const std::string dex_debug_name("__art_debug_dexfiles");
+  const std::string dex_debug_name("__dex_debug_descriptor");
   for (MapInfo* info : *maps) {
     if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
       continue;
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 4fc95c7..6ecedce 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -50,7 +50,17 @@
   memory_->set_cur_offset(start_offset);
   uint64_t cfa_offset;
   cur_pc_ = fde_->pc_start;
-  while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc_ <= pc) {
+  loc_regs->pc_start = cur_pc_;
+  while (true) {
+    if (cur_pc_ > pc) {
+      loc_regs->pc_end = cur_pc_;
+      return true;
+    }
+    if ((cfa_offset = memory_->cur_offset()) >= end_offset) {
+      loc_regs->pc_end = fde_->pc_end;
+      return true;
+    }
+    loc_regs->pc_start = cur_pc_;
     operands_.clear();
     // Read the cfa information.
     uint8_t cfa_value;
@@ -129,7 +139,6 @@
       }
     }
   }
-  return true;
 }
 
 template <typename AddressType>
@@ -424,7 +433,10 @@
 
 template <typename AddressType>
 bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
-  (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION,
+  // There is only one type of expression for CFA evaluation and the DWARF
+  // specification is unclear whether it returns the address or the
+  // dereferenced value. GDB expects the value, so will we.
+  (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
                           .values = {operands_[0], memory_->cur_offset()}};
   return true;
 }
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 7649798..65eec65 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -55,21 +55,29 @@
 }
 
 bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
-  last_error_.code = DWARF_ERROR_NONE;
-  const DwarfFde* fde = GetFdeFromPc(pc);
-  if (fde == nullptr || fde->cie == nullptr) {
-    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
-    return false;
-  }
+  // Lookup the pc in the cache.
+  auto it = loc_regs_.upper_bound(pc);
+  if (it == loc_regs_.end() || pc < it->second.pc_start) {
+    last_error_.code = DWARF_ERROR_NONE;
+    const DwarfFde* fde = GetFdeFromPc(pc);
+    if (fde == nullptr || fde->cie == nullptr) {
+      last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+      return false;
+    }
 
-  // Now get the location information for this pc.
-  dwarf_loc_regs_t loc_regs;
-  if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
-    return false;
+    // Now get the location information for this pc.
+    dwarf_loc_regs_t loc_regs;
+    if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+      return false;
+    }
+    loc_regs.cie = fde->cie;
+
+    // Store it in the cache.
+    it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
   }
 
   // Now eval the actual registers.
-  return Eval(fde->cie, process_memory, loc_regs, regs, finished);
+  return Eval(it->second.cie, process_memory, it->second, regs, finished);
 }
 
 template <typename AddressType>
@@ -190,8 +198,6 @@
   // Always set the dex pc to zero when evaluating.
   cur_regs->set_dex_pc(0);
 
-  AddressType prev_cfa = regs->sp();
-
   EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
                                   .cie = cie,
                                   .regular_memory = regular_memory,
@@ -204,31 +210,16 @@
         last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
         return false;
       }
-      // If the stack pointer register is the CFA, and the stack
-      // pointer register does not have any associated location
-      // information, use the current cfa value.
-      if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
-        eval_info.cfa = prev_cfa;
-      } else {
-        eval_info.cfa = (*cur_regs)[loc->values[0]];
-      }
+      eval_info.cfa = (*cur_regs)[loc->values[0]];
       eval_info.cfa += loc->values[1];
       break;
-    case DWARF_LOCATION_EXPRESSION:
     case DWARF_LOCATION_VAL_EXPRESSION: {
       AddressType value;
       if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
         return false;
       }
-      if (loc->type == DWARF_LOCATION_EXPRESSION) {
-        if (!regular_memory->ReadFully(value, &eval_info.cfa, sizeof(AddressType))) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = value;
-          return false;
-        }
-      } else {
-        eval_info.cfa = value;
-      }
+      // There is only one type of valid expression for CFA evaluation.
+      eval_info.cfa = value;
       break;
     }
     default:
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index dfb8e8f..f93baeb 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -127,13 +127,9 @@
   if (arm.ExtractEntryData(entry_offset) && arm.Eval()) {
     // If the pc was not set, then use the LR registers for the PC.
     if (!arm.pc_set()) {
-      regs_arm->set_pc((*regs_arm)[ARM_REG_LR]);
-      (*regs_arm)[ARM_REG_PC] = regs_arm->pc();
-    } else {
-      regs_arm->set_pc((*regs_arm)[ARM_REG_PC]);
+      (*regs_arm)[ARM_REG_PC] = (*regs_arm)[ARM_REG_LR];
     }
-    regs_arm->set_sp(arm.cfa());
-    (*regs_arm)[ARM_REG_SP] = regs_arm->sp();
+    (*regs_arm)[ARM_REG_SP] = arm.cfa();
     return_value = true;
 
     // If the pc was set to zero, consider this the final frame.
@@ -171,4 +167,17 @@
   return return_value;
 }
 
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+                                      uint64_t* offset) {
+  // For ARM, thumb function symbols have bit 0 set, but the address passed
+  // in here might not have this bit set and result in a failure to find
+  // the thumb function names. Adjust the address and offset to account
+  // for this possible case.
+  if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+    *offset &= ~1;
+    return true;
+  }
+  return false;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 9c067ba..c1597ce 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -76,6 +76,9 @@
   bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
                  bool* finished);
 
+  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+                       uint64_t* offset) override;
+
   uint64_t start_offset() { return start_offset_; }
 
   size_t total_entries() { return total_entries_; }
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 4c16212..e1a1a71 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/unique_fd.h>
 
+#include <algorithm>
 #include <cctype>
 #include <memory>
 #include <string>
@@ -209,6 +210,11 @@
   maps_.push_back(map_info);
 }
 
+void Maps::Sort() {
+  std::sort(maps_.begin(), maps_.end(),
+            [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
+}
+
 Maps::~Maps() {
   for (auto& map : maps_) {
     delete map;
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 5502ce1..27cab43 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -28,21 +28,46 @@
 
 namespace unwindstack {
 
-RegsArm::RegsArm()
-    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
+RegsArm::RegsArm() : RegsImpl<uint32_t>(ARM_REG_LAST, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
 
 ArchEnum RegsArm::Arch() {
   return ARCH_ARM;
 }
 
+uint64_t RegsArm::pc() {
+  return regs_[ARM_REG_PC];
+}
+
+uint64_t RegsArm::sp() {
+  return regs_[ARM_REG_SP];
+}
+
+void RegsArm::set_pc(uint64_t pc) {
+  regs_[ARM_REG_PC] = pc;
+}
+
+void RegsArm::set_sp(uint64_t sp) {
+  regs_[ARM_REG_SP] = sp;
+}
+
 uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return 2;
+  }
+
   uint64_t load_bias = elf->GetLoadBias();
   if (rel_pc < load_bias) {
-    return 0;
+    if (rel_pc < 2) {
+      return 0;
+    }
+    return 2;
   }
   uint64_t adjusted_rel_pc = rel_pc - load_bias;
   if (adjusted_rel_pc < 5) {
-    return 0;
+    if (adjusted_rel_pc < 2) {
+      return 0;
+    }
+    return 2;
   }
 
   if (adjusted_rel_pc & 1) {
@@ -56,17 +81,13 @@
   return 4;
 }
 
-void RegsArm::SetFromRaw() {
-  set_pc(regs_[ARM_REG_PC]);
-  set_sp(regs_[ARM_REG_SP]);
-}
-
 bool RegsArm::SetPcFromReturnAddress(Memory*) {
-  if (pc() == regs_[ARM_REG_LR]) {
+  uint32_t lr = regs_[ARM_REG_LR];
+  if (regs_[ARM_REG_PC] == lr) {
     return false;
   }
 
-  set_pc(regs_[ARM_REG_LR]);
+  regs_[ARM_REG_PC] = lr;
   return true;
 }
 
@@ -94,7 +115,6 @@
 
   RegsArm* regs = new RegsArm();
   memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -103,7 +123,6 @@
 
   RegsArm* regs = new RegsArm();
   memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -118,6 +137,7 @@
 
   uint64_t offset = 0;
   if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+    uint64_t sp = regs_[ARM_REG_SP];
     // non-RT sigreturn call.
     // __restore:
     //
@@ -131,17 +151,18 @@
     // Form 3 (thumb):
     // 0x77 0x27              movs r7, #77
     // 0x00 0xdf              svc 0
-    if (!process_memory->ReadFully(sp(), &data, sizeof(data))) {
+    if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
       return false;
     }
     if (data == 0x5ac3c35a) {
       // SP + uc_mcontext offset + r0 offset.
-      offset = sp() + 0x14 + 0xc;
+      offset = sp + 0x14 + 0xc;
     } else {
       // SP + r0 offset
-      offset = sp() + 0xc;
+      offset = sp + 0xc;
     }
   } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+    uint64_t sp = regs_[ARM_REG_SP];
     // RT sigreturn call.
     // __restore_rt:
     //
@@ -155,15 +176,15 @@
     // Form 3 (thumb):
     // 0xad 0x27              movs r7, #ad
     // 0x00 0xdf              svc 0
-    if (!process_memory->ReadFully(sp(), &data, sizeof(data))) {
+    if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
       return false;
     }
-    if (data == sp() + 8) {
+    if (data == sp + 8) {
       // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
-      offset = sp() + 8 + 0x80 + 0x14 + 0xc;
+      offset = sp + 8 + 0x80 + 0x14 + 0xc;
     } else {
       // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
-      offset = sp() + 0x80 + 0x14 + 0xc;
+      offset = sp + 0x80 + 0x14 + 0xc;
     }
   }
   if (offset == 0) {
@@ -173,7 +194,6 @@
   if (!process_memory->ReadFully(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
     return false;
   }
-  SetFromRaw();
   return true;
 }
 
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index cc6f5ce..4a2a6c4 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -29,30 +29,42 @@
 namespace unwindstack {
 
 RegsArm64::RegsArm64()
-    : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+    : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
 
 ArchEnum RegsArm64::Arch() {
   return ARCH_ARM64;
 }
 
-uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc < 4) {
+uint64_t RegsArm64::pc() {
+  return regs_[ARM64_REG_PC];
+}
+
+uint64_t RegsArm64::sp() {
+  return regs_[ARM64_REG_SP];
+}
+
+void RegsArm64::set_pc(uint64_t pc) {
+  regs_[ARM64_REG_PC] = pc;
+}
+
+void RegsArm64::set_sp(uint64_t sp) {
+  regs_[ARM64_REG_SP] = sp;
+}
+
+uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc < 4) {
     return 0;
   }
   return 4;
 }
 
-void RegsArm64::SetFromRaw() {
-  set_pc(regs_[ARM64_REG_PC]);
-  set_sp(regs_[ARM64_REG_SP]);
-}
-
 bool RegsArm64::SetPcFromReturnAddress(Memory*) {
-  if (pc() == regs_[ARM64_REG_LR]) {
+  uint64_t lr = regs_[ARM64_REG_LR];
+  if (regs_[ARM64_REG_PC] == lr) {
     return false;
   }
 
-  set_pc(regs_[ARM64_REG_LR]);
+  regs_[ARM64_REG_PC] = lr;
   return true;
 }
 
@@ -100,7 +112,6 @@
   uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
   reg_data[ARM64_REG_PC] = user->pc;
   reg_data[ARM64_REG_SP] = user->sp;
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -109,7 +120,6 @@
 
   RegsArm64* regs = new RegsArm64();
   memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -131,12 +141,10 @@
   }
 
   // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
-  if (!process_memory->ReadFully(sp() + 0x80 + 0xb0 + 0x08, regs_.data(),
+  if (!process_memory->ReadFully(regs_[ARM64_REG_SP] + 0x80 + 0xb0 + 0x08, regs_.data(),
                                  sizeof(uint64_t) * ARM64_REG_LAST)) {
     return false;
   }
-
-  SetFromRaw();
   return true;
 }
 
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 5d20bef..c87e69b 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -29,31 +29,43 @@
 namespace unwindstack {
 
 RegsMips::RegsMips()
-    : RegsImpl<uint32_t>(MIPS_REG_LAST, MIPS_REG_SP, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
+    : RegsImpl<uint32_t>(MIPS_REG_LAST, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
 
 ArchEnum RegsMips::Arch() {
   return ARCH_MIPS;
 }
 
-uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc < 8) {
+uint64_t RegsMips::pc() {
+  return regs_[MIPS_REG_PC];
+}
+
+uint64_t RegsMips::sp() {
+  return regs_[MIPS_REG_SP];
+}
+
+void RegsMips::set_pc(uint64_t pc) {
+  regs_[MIPS_REG_PC] = static_cast<uint32_t>(pc);
+}
+
+void RegsMips::set_sp(uint64_t sp) {
+  regs_[MIPS_REG_SP] = static_cast<uint32_t>(sp);
+}
+
+uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc < 8) {
     return 0;
   }
   // For now, just assume no compact branches
   return 8;
 }
 
-void RegsMips::SetFromRaw() {
-  set_pc(regs_[MIPS_REG_PC]);
-  set_sp(regs_[MIPS_REG_SP]);
-}
-
 bool RegsMips::SetPcFromReturnAddress(Memory*) {
-  if (pc() == regs_[MIPS_REG_RA]) {
+  uint32_t ra = regs_[MIPS_REG_RA];
+  if (regs_[MIPS_REG_PC] == ra) {
     return false;
   }
 
-  set_pc(regs_[MIPS_REG_RA]);
+  regs_[MIPS_REG_PC] = ra;
   return true;
 }
 
@@ -101,7 +113,6 @@
   memcpy(regs->RawData(), &user->regs[MIPS32_EF_R0], (MIPS_REG_R31 + 1) * sizeof(uint32_t));
 
   reg_data[MIPS_REG_PC] = user->regs[MIPS32_EF_CP0_EPC];
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -114,7 +125,6 @@
       (*regs)[MIPS_REG_R0 + i] = mips_ucontext->uc_mcontext.sc_regs[i];
   }
   (*regs)[MIPS_REG_PC] = mips_ucontext->uc_mcontext.sc_pc;
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -149,7 +159,7 @@
 
   // read sc_pc and sc_regs[32] from stack
   uint64_t values[MIPS_REG_LAST];
-  if (!process_memory->Read(sp() + offset, values, sizeof(values))) {
+  if (!process_memory->Read(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
     return false;
   }
 
@@ -160,8 +170,6 @@
   for (int i = 0; i < 32; i++) {
       regs_[MIPS_REG_R0 + i] = values[1 + i];
   }
-
-  SetFromRaw();
   return true;
 }
 
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 4a03538..236a922 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -29,32 +29,43 @@
 namespace unwindstack {
 
 RegsMips64::RegsMips64()
-    : RegsImpl<uint64_t>(MIPS64_REG_LAST, MIPS64_REG_SP,
-                         Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
+    : RegsImpl<uint64_t>(MIPS64_REG_LAST, Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
 
 ArchEnum RegsMips64::Arch() {
   return ARCH_MIPS64;
 }
 
-uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc < 8) {
+uint64_t RegsMips64::pc() {
+  return regs_[MIPS64_REG_PC];
+}
+
+uint64_t RegsMips64::sp() {
+  return regs_[MIPS64_REG_SP];
+}
+
+void RegsMips64::set_pc(uint64_t pc) {
+  regs_[MIPS64_REG_PC] = pc;
+}
+
+void RegsMips64::set_sp(uint64_t sp) {
+  regs_[MIPS64_REG_SP] = sp;
+}
+
+uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc < 8) {
     return 0;
   }
   // For now, just assume no compact branches
   return 8;
 }
 
-void RegsMips64::SetFromRaw() {
-  set_pc(regs_[MIPS64_REG_PC]);
-  set_sp(regs_[MIPS64_REG_SP]);
-}
-
 bool RegsMips64::SetPcFromReturnAddress(Memory*) {
-  if (pc() == regs_[MIPS64_REG_RA]) {
+  uint64_t ra = regs_[MIPS64_REG_RA];
+  if (regs_[MIPS64_REG_PC] == ra) {
     return false;
   }
 
-  set_pc(regs_[MIPS64_REG_RA]);
+  regs_[MIPS64_REG_PC] = ra;
   return true;
 }
 
@@ -102,7 +113,6 @@
   memcpy(regs->RawData(), &user->regs[MIPS64_EF_R0], (MIPS64_REG_R31 + 1) * sizeof(uint64_t));
 
   reg_data[MIPS64_REG_PC] = user->regs[MIPS64_EF_CP0_EPC];
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -113,7 +123,6 @@
   // Copy 64 bit sc_regs over to 64 bit regs
   memcpy(regs->RawData(), &mips64_ucontext->uc_mcontext.sc_regs[0], 32 * sizeof(uint64_t));
   (*regs)[MIPS64_REG_PC] = mips64_ucontext->uc_mcontext.sc_pc;
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -137,19 +146,17 @@
   // vdso_rt_sigreturn => read rt_sigframe
   // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset
   // read 64 bit sc_regs[32] from stack into 64 bit regs_
-  if (!process_memory->Read(sp() + 24 + 128 + 40, regs_.data(),
+  uint64_t sp = regs_[MIPS64_REG_SP];
+  if (!process_memory->Read(sp + 24 + 128 + 40, regs_.data(),
                             sizeof(uint64_t) * (MIPS64_REG_LAST - 1))) {
     return false;
   }
 
   // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
   // read 64 bit sc_pc from stack into 64 bit regs_[MIPS64_REG_PC]
-  if (!process_memory->Read(sp() + 24 + 128 + 40 + 576, &regs_[MIPS64_REG_PC],
-                            sizeof(uint64_t))) {
+  if (!process_memory->Read(sp + 24 + 128 + 40 + 576, &regs_[MIPS64_REG_PC], sizeof(uint64_t))) {
     return false;
   }
-
-  SetFromRaw();
   return true;
 }
 
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index 573cb23..f7e0614 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -28,33 +28,44 @@
 
 namespace unwindstack {
 
-RegsX86::RegsX86()
-    : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
+RegsX86::RegsX86() : RegsImpl<uint32_t>(X86_REG_LAST, Location(LOCATION_SP_OFFSET, -4)) {}
 
 ArchEnum RegsX86::Arch() {
   return ARCH_X86;
 }
 
-uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc == 0) {
+uint64_t RegsX86::pc() {
+  return regs_[X86_REG_PC];
+}
+
+uint64_t RegsX86::sp() {
+  return regs_[X86_REG_SP];
+}
+
+void RegsX86::set_pc(uint64_t pc) {
+  regs_[X86_REG_PC] = static_cast<uint32_t>(pc);
+}
+
+void RegsX86::set_sp(uint64_t sp) {
+  regs_[X86_REG_SP] = static_cast<uint32_t>(sp);
+}
+
+uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc == 0) {
     return 0;
   }
   return 1;
 }
 
-void RegsX86::SetFromRaw() {
-  set_pc(regs_[X86_REG_PC]);
-  set_sp(regs_[X86_REG_SP]);
-}
-
 bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
   // Attempt to get the return address from the top of the stack.
   uint32_t new_pc;
-  if (!process_memory->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+  if (!process_memory->ReadFully(regs_[X86_REG_SP], &new_pc, sizeof(new_pc)) ||
+      new_pc == regs_[X86_REG_PC]) {
     return false;
   }
 
-  set_pc(new_pc);
+  regs_[X86_REG_PC] = new_pc;
   return true;
 }
 
@@ -84,7 +95,6 @@
   (*regs)[X86_REG_ESP] = user->esp;
   (*regs)[X86_REG_EIP] = user->eip;
 
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -99,7 +109,6 @@
   regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
   regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
   regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
-  SetFromRaw();
 }
 
 Regs* RegsX86::CreateFromUcontext(void* ucontext) {
@@ -131,7 +140,7 @@
     //   int signum
     //   struct sigcontext (same format as mcontext)
     struct x86_mcontext_t context;
-    if (!process_memory->ReadFully(sp() + 4, &context, sizeof(context))) {
+    if (!process_memory->ReadFully(regs_[X86_REG_SP] + 4, &context, sizeof(context))) {
       return false;
     }
     regs_[X86_REG_EBP] = context.ebp;
@@ -141,7 +150,6 @@
     regs_[X86_REG_ECX] = context.ecx;
     regs_[X86_REG_EAX] = context.eax;
     regs_[X86_REG_EIP] = context.eip;
-    SetFromRaw();
     return true;
   } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
     // With SA_SIGINFO set, the return sequence is:
@@ -157,7 +165,7 @@
 
     // Get the location of the sigcontext data.
     uint32_t ptr;
-    if (!process_memory->ReadFully(sp() + 8, &ptr, sizeof(ptr))) {
+    if (!process_memory->ReadFully(regs_[X86_REG_SP] + 8, &ptr, sizeof(ptr))) {
       return false;
     }
     // Only read the portion of the data structure we care about.
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index 3175a90..7d6ad86 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -28,33 +28,44 @@
 
 namespace unwindstack {
 
-RegsX86_64::RegsX86_64()
-    : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
+RegsX86_64::RegsX86_64() : RegsImpl<uint64_t>(X86_64_REG_LAST, Location(LOCATION_SP_OFFSET, -8)) {}
 
 ArchEnum RegsX86_64::Arch() {
   return ARCH_X86_64;
 }
 
-uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc == 0) {
+uint64_t RegsX86_64::pc() {
+  return regs_[X86_64_REG_PC];
+}
+
+uint64_t RegsX86_64::sp() {
+  return regs_[X86_64_REG_SP];
+}
+
+void RegsX86_64::set_pc(uint64_t pc) {
+  regs_[X86_64_REG_PC] = pc;
+}
+
+void RegsX86_64::set_sp(uint64_t sp) {
+  regs_[X86_64_REG_SP] = sp;
+}
+
+uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc == 0) {
     return 0;
   }
   return 1;
 }
 
-void RegsX86_64::SetFromRaw() {
-  set_pc(regs_[X86_64_REG_PC]);
-  set_sp(regs_[X86_64_REG_SP]);
-}
-
 bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
   // Attempt to get the return address from the top of the stack.
   uint64_t new_pc;
-  if (!process_memory->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+  if (!process_memory->ReadFully(regs_[X86_64_REG_SP], &new_pc, sizeof(new_pc)) ||
+      new_pc == regs_[X86_64_REG_PC]) {
     return false;
   }
 
-  set_pc(new_pc);
+  regs_[X86_64_REG_PC] = new_pc;
   return true;
 }
 
@@ -100,7 +111,6 @@
   (*regs)[X86_64_REG_RSP] = user->rsp;
   (*regs)[X86_64_REG_RIP] = user->rip;
 
-  regs->SetFromRaw();
   return regs;
 }
 
@@ -118,8 +128,6 @@
   regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
   regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
   regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
-
-  SetFromRaw();
 }
 
 Regs* RegsX86_64::CreateFromUcontext(void* ucontext) {
@@ -152,7 +160,7 @@
   // Read the mcontext data from the stack.
   // sp points to the ucontext data structure, read only the mcontext part.
   x86_64_ucontext_t x86_64_ucontext;
-  if (!process_memory->ReadFully(sp() + 0x28, &x86_64_ucontext.uc_mcontext,
+  if (!process_memory->ReadFully(regs_[X86_64_REG_SP] + 0x28, &x86_64_ucontext.uc_mcontext,
                                  sizeof(x86_64_mcontext_t))) {
     return false;
   }
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 7da6994..9a6c6df 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -29,6 +29,7 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Unwinder.h>
 
 #if !defined(NO_LIBDEXFILE_SUPPORT)
@@ -61,7 +62,9 @@
     frame->map_offset = info->offset;
     frame->map_load_bias = info->load_bias;
     frame->map_flags = info->flags;
-    frame->map_name = info->name;
+    if (resolve_names_) {
+      frame->map_name = info->name;
+    }
     frame->rel_pc = dex_pc - info->start;
   } else {
     frame->rel_pc = dex_pc;
@@ -77,7 +80,6 @@
     return;
   }
 
-  // dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
   dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
                                    &frame->function_offset);
 #endif
@@ -97,7 +99,9 @@
     return;
   }
 
-  frame->map_name = map_info->name;
+  if (resolve_names_) {
+    frame->map_name = map_info->name;
+  }
   frame->map_offset = map_info->offset;
   frame->map_start = map_info->start;
   frame->map_end = map_info->end;
@@ -139,26 +143,31 @@
     uint64_t cur_sp = regs_->sp();
 
     MapInfo* map_info = maps_->Find(regs_->pc());
-    uint64_t rel_pc;
     uint64_t pc_adjustment = 0;
     uint64_t step_pc;
+    uint64_t rel_pc;
     Elf* elf;
     if (map_info == nullptr) {
-      rel_pc = regs_->pc();
-      step_pc = rel_pc;
+      step_pc = regs_->pc();
+      rel_pc = step_pc;
       last_error_.code = ERROR_INVALID_MAP;
     } else {
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
       }
       elf = map_info->GetElf(process_memory_, true);
-      rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+      step_pc = regs_->pc();
+      rel_pc = elf->GetRelPc(step_pc, map_info);
+      // Everyone except elf data in gdb jit debug maps uses the relative pc.
+      if (!(map_info->flags & MAPS_FLAGS_JIT_SYMFILE_MAP)) {
+        step_pc = rel_pc;
+      }
       if (adjust_pc) {
         pc_adjustment = regs_->GetPcAdjustment(rel_pc, elf);
       } else {
         pc_adjustment = 0;
       }
-      step_pc = rel_pc - pc_adjustment;
+      step_pc -= pc_adjustment;
 
       // If the pc is in an invalid elf file, try and get an Elf object
       // using the jit debug information.
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 0881182..3d50ccf 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -23,6 +23,8 @@
 
 namespace unwindstack {
 
+struct DwarfCie;
+
 enum DwarfLocationEnum : uint8_t {
   DWARF_LOCATION_INVALID = 0,
   DWARF_LOCATION_UNDEFINED,
@@ -38,7 +40,13 @@
   uint64_t values[2];
 };
 
-typedef std::unordered_map<uint32_t, DwarfLocation> dwarf_loc_regs_t;
+struct DwarfLocations : public std::unordered_map<uint32_t, DwarfLocation> {
+  const DwarfCie* cie;
+  // The range of PCs where the locations are valid (end is exclusive).
+  uint64_t pc_start = 0;
+  uint64_t pc_end = 0;
+};
+typedef DwarfLocations dwarf_loc_regs_t;
 
 }  // namespace unwindstack
 
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index da91fd0..209c54a 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <iterator>
+#include <map>
 #include <unordered_map>
 
 #include <unwindstack/DwarfError.h>
@@ -112,6 +113,7 @@
   std::unordered_map<uint64_t, DwarfFde> fde_entries_;
   std::unordered_map<uint64_t, DwarfCie> cie_entries_;
   std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+  std::map<uint64_t, dwarf_loc_regs_t> loc_regs_;  // Single row indexed by pc_end.
 };
 
 template <typename AddressType>
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 17a2d28..74e5c47 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -30,6 +30,10 @@
 // Special flag to indicate a map is in /dev/. However, a map in
 // /dev/ashmem/... does not set this flag.
 static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+// Special flag to indicate that this map represents an elf file
+// created by ART for use with the gdb jit debug interface.
+// This should only ever appear in offline maps data.
+static constexpr int MAPS_FLAGS_JIT_SYMFILE_MAP = 0x4000;
 
 class Maps {
  public:
@@ -45,6 +49,8 @@
   void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
            uint64_t load_bias);
 
+  void Sort();
+
   typedef std::vector<MapInfo*>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index b0e7ea1..4bac473 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -45,8 +45,8 @@
     int16_t value;
   };
 
-  Regs(uint16_t total_regs, uint16_t sp_reg, const Location& return_loc)
-      : total_regs_(total_regs), sp_reg_(sp_reg), return_loc_(return_loc) {}
+  Regs(uint16_t total_regs, const Location& return_loc)
+      : total_regs_(total_regs), return_loc_(return_loc) {}
   virtual ~Regs() = default;
 
   virtual ArchEnum Arch() = 0;
@@ -57,6 +57,9 @@
   virtual uint64_t pc() = 0;
   virtual uint64_t sp() = 0;
 
+  virtual void set_pc(uint64_t pc) = 0;
+  virtual void set_sp(uint64_t sp) = 0;
+
   uint64_t dex_pc() { return dex_pc_; }
   void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
 
@@ -64,13 +67,10 @@
 
   virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
 
-  virtual void SetFromRaw() = 0;
-
   virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
 
   virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) = 0;
 
-  uint16_t sp_reg() { return sp_reg_; }
   uint16_t total_regs() { return total_regs_; }
 
   static ArchEnum CurrentArch();
@@ -80,7 +80,6 @@
 
  protected:
   uint16_t total_regs_;
-  uint16_t sp_reg_;
   Location return_loc_;
   uint64_t dex_pc_ = 0;
 };
@@ -88,16 +87,10 @@
 template <typename AddressType>
 class RegsImpl : public Regs {
  public:
-  RegsImpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
-      : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
+  RegsImpl(uint16_t total_regs, Location return_loc)
+      : Regs(total_regs, return_loc), regs_(total_regs) {}
   virtual ~RegsImpl() = default;
 
-  uint64_t pc() override { return pc_; }
-  uint64_t sp() override { return sp_; }
-
-  void set_pc(AddressType pc) { pc_ = pc; }
-  void set_sp(AddressType sp) { sp_ = sp; }
-
   bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
 
   inline AddressType& operator[](size_t reg) { return regs_[reg]; }
@@ -111,8 +104,6 @@
   }
 
  protected:
-  AddressType pc_;
-  AddressType sp_;
   std::vector<AddressType> regs_;
 };
 
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index 5af90d3..31e6797 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -34,17 +34,21 @@
   RegsArm();
   virtual ~RegsArm() = default;
 
-  virtual ArchEnum Arch() override final;
+  ArchEnum Arch() override final;
 
   uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
-  void SetFromRaw() override;
-
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  uint64_t pc() override;
+  uint64_t sp() override;
+
+  void set_pc(uint64_t pc) override;
+  void set_sp(uint64_t sp) override;
 
   static Regs* Read(void* data);
 
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index cb05732..0c45eba 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -34,17 +34,21 @@
   RegsArm64();
   virtual ~RegsArm64() = default;
 
-  virtual ArchEnum Arch() override final;
+  ArchEnum Arch() override final;
 
   uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
-  void SetFromRaw() override;
-
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  uint64_t pc() override;
+  uint64_t sp() override;
+
+  void set_pc(uint64_t pc) override;
+  void set_sp(uint64_t sp) override;
 
   static Regs* Read(void* data);
 
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index 557eace..81c0af3 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -51,8 +51,6 @@
       : [base] "+r"(reg_data)
       :
       : "memory");
-
-  regs->SetFromRaw();
 }
 
 #elif defined(__aarch64__)
@@ -83,8 +81,6 @@
       : [base] "+r"(reg_data)
       :
       : "x12", "x13", "memory");
-
-  regs->SetFromRaw();
 }
 
 #elif defined(__i386__) || defined(__x86_64__) || defined(__mips__)
@@ -93,8 +89,6 @@
 
 inline void RegsGetLocal(Regs* regs) {
   AsmGetRegs(regs->RawData());
-
-  regs->SetFromRaw();
 }
 
 #endif
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index 8e3c01f..709f9e2 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -34,17 +34,21 @@
   RegsMips();
   virtual ~RegsMips() = default;
 
-  virtual ArchEnum Arch() override final;
+  ArchEnum Arch() override final;
 
   uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
-  void SetFromRaw() override;
-
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  uint64_t pc() override;
+  uint64_t sp() override;
+
+  void set_pc(uint64_t pc) override;
+  void set_sp(uint64_t sp) override;
 
   static Regs* Read(void* data);
 
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 8c2d443..1de83ea 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -34,17 +34,21 @@
   RegsMips64();
   virtual ~RegsMips64() = default;
 
-  virtual ArchEnum Arch() override final;
+  ArchEnum Arch() override final;
 
   uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
-  void SetFromRaw() override;
-
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  uint64_t pc() override;
+  uint64_t sp() override;
+
+  void set_pc(uint64_t pc) override;
+  void set_sp(uint64_t sp) override;
 
   static Regs* Read(void* data);
 
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index 1bc145d..586c9d8 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -35,19 +35,23 @@
   RegsX86();
   virtual ~RegsX86() = default;
 
-  virtual ArchEnum Arch() override final;
+  ArchEnum Arch() override final;
 
   uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
-  void SetFromRaw() override;
-
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
   void SetFromUcontext(x86_ucontext_t* ucontext);
 
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  uint64_t pc() override;
+  uint64_t sp() override;
+
+  void set_pc(uint64_t pc) override;
+  void set_sp(uint64_t sp) override;
 
   static Regs* Read(void* data);
 
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index 4cd45d4..061f479 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -35,19 +35,23 @@
   RegsX86_64();
   virtual ~RegsX86_64() = default;
 
-  virtual ArchEnum Arch() override final;
+  ArchEnum Arch() override final;
 
   uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
-  void SetFromRaw() override;
-
   bool SetPcFromReturnAddress(Memory* process_memory) override;
 
   bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
 
   void SetFromUcontext(x86_64_ucontext_t* ucontext);
 
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  uint64_t pc() override;
+  uint64_t sp() override;
+
+  void set_pc(uint64_t pc) override;
+  void set_sp(uint64_t sp) override;
 
   static Regs* Read(void* data);
 
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index dca5605..d029bb0 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -63,7 +63,7 @@
     elf->FakeSetValid(true);
     ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
 
     // Global variable not set by default.
@@ -74,7 +74,7 @@
     elf->FakeSetValid(true);
     interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
 
     // Global variable set in this map.
@@ -85,10 +85,12 @@
     elf->FakeSetValid(true);
     interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
   }
 
+  void WriteDescriptor32(uint64_t addr, uint32_t head);
+  void WriteDescriptor64(uint64_t addr, uint64_t head);
   void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
   void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
   void WriteDex(uint64_t dex_file);
@@ -105,6 +107,16 @@
   std::unique_ptr<BufferMaps> maps_;
 };
 
+void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t head) {
+  //   void* first_entry_
+  memory_->SetData32(addr + 12, head);
+}
+
+void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) {
+  //   void* first_entry_
+  memory_->SetData64(addr + 16, head);
+}
+
 void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
                                 uint32_t dex_file) {
   // Format of the 32 bit DEXFileEntry structure:
@@ -146,7 +158,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  memory_->SetData32(0xf800, 0x200000);
+  WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -161,7 +173,7 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   dex_files_->SetArch(ARCH_ARM64);
-  memory_->SetData64(0xf800, 0x200000);
+  WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x301000);
   WriteDex(0x301000);
 
@@ -175,7 +187,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  memory_->SetData32(0xf800, 0x200000);
+  WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32(0x200000, 0x200100, 0, 0x100000);
   WriteEntry32(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
@@ -191,7 +203,7 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   dex_files_->SetArch(ARCH_ARM64);
-  memory_->SetData64(0xf800, 0x200000);
+  WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0x200100, 0, 0x100000);
   WriteEntry64(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
@@ -206,7 +218,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  memory_->SetData32(0xf800, 0x200000);
+  WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -226,7 +238,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  memory_->SetData32(0xf800, 0x200000);
+  WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32(0x200000, 0x200100, 0, 0x100000);
   WriteEntry32(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
@@ -259,9 +271,9 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
-  memory_->SetData32(0xc800, 0);
+  WriteDescriptor32(0xc800, 0);
 
-  memory_->SetData32(0xf800, 0x200000);
+  WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -274,7 +286,7 @@
   dex_files_->SetArch(ARCH_ARM);
   method_name = "fail";
   method_offset = 0x123;
-  memory_->SetData32(0xc800, 0x100000);
+  WriteDescriptor32(0xc800, 0x100000);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
@@ -286,9 +298,9 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
-  memory_->SetData64(0xc800, 0);
+  WriteDescriptor64(0xc800, 0);
 
-  memory_->SetData64(0xf800, 0x200000);
+  WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -302,7 +314,7 @@
   dex_files_->SetArch(ARCH_ARM64);
   method_name = "fail";
   method_offset = 0x123;
-  memory_->SetData32(0xc800, 0x100000);
+  WriteDescriptor64(0xc800, 0x100000);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 68dc30c..7395b04 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -737,6 +737,8 @@
   ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
   ASSERT_EQ(0x284U, this->dmem_->cur_offset());
   ASSERT_EQ(1U, loc_regs.size());
+  ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, loc_regs[CFA_REG].type);
+  ASSERT_EQ(0x81U, loc_regs[CFA_REG].values[0]);
 
   ASSERT_EQ("", GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 6e15227..d424d5f 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -1468,7 +1468,7 @@
   }
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsImplFake<TypeParam> regs(32, 10);
+  RegsImplFake<TypeParam> regs(32);
   for (size_t i = 0; i < 32; i++) {
     regs[i] = i + 10;
   }
@@ -1499,7 +1499,7 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsImplFake<TypeParam> regs(16, 10);
+  RegsImplFake<TypeParam> regs(16);
   for (size_t i = 0; i < 16; i++) {
     regs[i] = i + 10;
   }
@@ -1526,7 +1526,7 @@
                                         0x92, 0x80, 0x15, 0x80, 0x02};
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsImplFake<TypeParam> regs(10, 10);
+  RegsImplFake<TypeParam> regs(10);
   regs[5] = 0x45;
   regs[6] = 0x190;
   RegsInfo<TypeParam> regs_info(&regs);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 37305b2..c340291 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -92,14 +92,14 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
   regs.set_sp(0x2000);
   regs[5] = 0x20;
   regs[9] = 0x3000;
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
@@ -108,7 +108,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -116,7 +116,7 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->LastErrorCode());
@@ -124,7 +124,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -136,15 +136,13 @@
   this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
   bool finished;
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_FALSE(finished);
-  EXPECT_EQ(0x12345U, regs.sp());
-  EXPECT_EQ(0x20U, regs.pc());
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -162,7 +160,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -170,7 +168,7 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->LastErrorCode());
@@ -178,7 +176,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
   DwarfCie cie{.return_address_register = 60};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   bool finished;
@@ -188,7 +186,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   bool finished;
@@ -198,7 +196,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
@@ -227,7 +225,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -239,12 +237,12 @@
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
-  EXPECT_EQ(0x2000U, regs.sp());
+  EXPECT_EQ(0x3000U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -262,7 +260,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -283,7 +281,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_register_reference_chain) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -314,7 +312,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_dex_pc) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -333,7 +331,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -348,7 +346,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   if (sizeof(TypeParam) == sizeof(uint64_t)) {
@@ -382,7 +380,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -400,7 +398,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_pc_zero) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -417,7 +415,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -434,7 +432,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
   DwarfCie cie{.return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -453,7 +451,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -473,7 +471,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsImplFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -855,7 +853,8 @@
   fde.cfa_instructions_offset = 0x6000;
   fde.cfa_instructions_end = 0x6002;
 
-  dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+  dwarf_loc_regs_t cie_loc_regs;
+  cie_loc_regs[6] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 0}};
   this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
   this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
 
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 3fcd2b6..071d2df 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -165,4 +165,72 @@
   ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
 }
 
+static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
+                                   dwarf_loc_regs_t* loc_regs) {
+  loc_regs->pc_start = fde->pc_start;
+  loc_regs->pc_end = fde->pc_end;
+  return true;
+}
+
+TEST_F(DwarfSectionTest, Step_cache) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_start = 0x500;
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x1500, nullptr, &process, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde0{};
+  fde0.pc_start = 0x1000;
+  fde0.pc_end = 0x2000;
+  fde0.cie = &cie;
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde0));
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+
+  DwarfFde fde1{};
+  fde1.pc_start = 0x500;
+  fde1.pc_end = 0x800;
+  fde1.cie = &cie;
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x600, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde1));
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  ASSERT_TRUE(mock_section.Step(0x600, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x700, nullptr, &process, &finished));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index ae9da5e..66207db 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -65,8 +65,8 @@
   }
 
   RegsFake* fake_regs = reinterpret_cast<RegsFake*>(regs);
-  fake_regs->FakeSetPc(entry.pc);
-  fake_regs->FakeSetSp(entry.sp);
+  fake_regs->set_pc(entry.pc);
+  fake_regs->set_sp(entry.sp);
   *finished = entry.finished;
   return true;
 }
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index eb85033..f9028c4 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -316,7 +316,6 @@
   RegsArm regs;
   regs[13] = 0x50000;
   regs[15] = 0x8000;
-  regs.SetFromRaw();
 
   ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
   elf.FakeSetInterface(interface);
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index ab23194..ede16b3 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -27,14 +27,16 @@
 
 class RegsFake : public Regs {
  public:
-  RegsFake(uint16_t total_regs, uint16_t sp_reg)
-      : Regs(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  RegsFake(uint16_t total_regs) : Regs(total_regs, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
   virtual ~RegsFake() = default;
 
   ArchEnum Arch() override { return fake_arch_; }
   void* RawData() override { return nullptr; }
   uint64_t pc() override { return fake_pc_; }
   uint64_t sp() override { return fake_sp_; }
+  void set_pc(uint64_t pc) override { fake_pc_ = pc; }
+  void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+
   bool SetPcFromReturnAddress(Memory*) override {
     if (!fake_return_address_valid_) {
       return false;
@@ -51,11 +53,7 @@
 
   bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
 
-  void SetFromRaw() override {}
-
   void FakeSetArch(ArchEnum arch) { fake_arch_ = arch; }
-  void FakeSetPc(uint64_t pc) { fake_pc_ = pc; }
-  void FakeSetSp(uint64_t sp) { fake_sp_ = sp; }
   void FakeSetDexPc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
   void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
   void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
@@ -71,19 +69,23 @@
 template <typename TypeParam>
 class RegsImplFake : public RegsImpl<TypeParam> {
  public:
-  RegsImplFake(uint16_t total_regs, uint16_t sp_reg)
-      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  RegsImplFake(uint16_t total_regs)
+      : RegsImpl<TypeParam>(total_regs, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
   virtual ~RegsImplFake() = default;
 
   ArchEnum Arch() override { return ARCH_UNKNOWN; }
+  uint64_t pc() override { return fake_pc_; }
+  uint64_t sp() override { return fake_sp_; }
+  void set_pc(uint64_t pc) override { fake_pc_ = pc; }
+  void set_sp(uint64_t sp) override { fake_sp_ = sp; }
 
   uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 0; }
-  void SetFromRaw() override {}
   bool SetPcFromReturnAddress(Memory*) override { return false; }
   bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
 
-  void FakeSetPc(uint64_t pc) { this->pc_ = pc; }
-  void FakeSetSp(uint64_t sp) { this->sp_ = sp; }
+ private:
+  uint64_t fake_pc_ = 0;
+  uint64_t fake_sp_ = 0;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index ecd4051..eac12ca 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -56,7 +56,6 @@
   RegsArm regs;
   regs[ARM_REG_PC] = 0x5000;
   regs[ARM_REG_SP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData32(0x5000, pc_data);
 
@@ -87,7 +86,6 @@
   RegsArm regs;
   regs[ARM_REG_PC] = 0x5000;
   regs[ARM_REG_SP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData32(0x5000, pc_data);
 
@@ -118,7 +116,6 @@
   RegsArm64 regs;
   regs[ARM64_REG_PC] = 0x8000;
   regs[ARM64_REG_SP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
 
@@ -138,7 +135,6 @@
   RegsX86 regs;
   regs[X86_REG_EIP] = 0x4100;
   regs[X86_REG_ESP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
   for (uint64_t index = 0; index <= 25; index++) {
@@ -162,7 +158,6 @@
   RegsX86 regs;
   regs[X86_REG_EIP] = 0x4100;
   regs[X86_REG_ESP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
   addr += 8;
@@ -191,7 +186,6 @@
   RegsX86_64 regs;
   regs[X86_64_REG_RIP] = 0x7000;
   regs[X86_64_REG_RSP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748);
   elf_memory_->SetData16(0x7008, 0x0f05);
@@ -212,7 +206,6 @@
   RegsMips regs;
   regs[MIPS_REG_PC] = 0x8000;
   regs[MIPS_REG_SP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData64(0x8000, 0x0000000c24021017ULL);
 
@@ -232,7 +225,6 @@
   RegsMips regs;
   regs[MIPS_REG_PC] = 0x8000;
   regs[MIPS_REG_SP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData64(0x8000, 0x0000000c24021061ULL);
 
@@ -252,7 +244,6 @@
   RegsMips64 regs;
   regs[MIPS64_REG_PC] = 0x8000;
   regs[MIPS64_REG_SP] = addr;
-  regs.SetFromRaw();
 
   elf_memory_->SetData64(0x8000, 0x0000000c2402145bULL);
 
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 8b2f6c8..d15823e 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -49,9 +49,8 @@
 };
 
 TEST_F(RegsTest, regs32) {
-  RegsImplFake<uint32_t> regs32(50, 10);
+  RegsImplFake<uint32_t> regs32(50);
   ASSERT_EQ(50U, regs32.total_regs());
-  ASSERT_EQ(10U, regs32.sp_reg());
 
   uint32_t* raw = reinterpret_cast<uint32_t*>(regs32.RawData());
   for (size_t i = 0; i < 50; i++) {
@@ -72,9 +71,8 @@
 }
 
 TEST_F(RegsTest, regs64) {
-  RegsImplFake<uint64_t> regs64(30, 12);
+  RegsImplFake<uint64_t> regs64(30);
   ASSERT_EQ(30U, regs64.total_regs());
-  ASSERT_EQ(12U, regs64.sp_reg());
 
   uint64_t* raw = reinterpret_cast<uint64_t*>(regs64.RawData());
   for (size_t i = 0; i < 30; i++) {
@@ -96,48 +94,48 @@
 
 TEST_F(RegsTest, rel_pc) {
   RegsArm64 arm64;
-  ASSERT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
-  ASSERT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
+  EXPECT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
 
   RegsX86 x86;
-  ASSERT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
-  ASSERT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
+  EXPECT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
 
   RegsX86_64 x86_64;
-  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
-  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
+  EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
 
   RegsMips mips;
-  ASSERT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
-  ASSERT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
+  EXPECT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
 
   RegsMips64 mips64;
-  ASSERT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
-  ASSERT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
+  EXPECT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
 }
 
 TEST_F(RegsTest, rel_pc_arm) {
@@ -145,34 +143,36 @@
 
   // Check fence posts.
   elf_->FakeSetLoadBias(0);
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
 
   elf_->FakeSetLoadBias(0x100);
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0xff, elf_.get()));
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x104, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x103, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x102, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0xff, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x104, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x103, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x102, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
 
   // Check thumb instructions handling.
   elf_->FakeSetLoadBias(0);
   memory_->SetData32(0x2000, 0);
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
   memory_->SetData32(0x2000, 0xe000f000);
-  ASSERT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
+  EXPECT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
 
   elf_->FakeSetLoadBias(0x400);
   memory_->SetData32(0x2100, 0);
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
   memory_->SetData32(0x2100, 0xf111f111);
-  ASSERT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
+  EXPECT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
 }
 
 TEST_F(RegsTest, elf_invalid) {
@@ -183,90 +183,85 @@
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
   MapInfo map_info(0x1000, 0x2000);
-  Elf* invalid_elf = new Elf(new MemoryFake);
+  Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
   regs_arm.set_pc(0x1500);
   EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
-  EXPECT_EQ(4U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+  EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+  EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x511U, invalid_elf));
 
   regs_arm64.set_pc(0x1600);
   EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
-  EXPECT_EQ(0U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
+  EXPECT_EQ(4U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
 
   regs_x86.set_pc(0x1700);
   EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
-  EXPECT_EQ(0U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
+  EXPECT_EQ(1U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
 
   regs_x86_64.set_pc(0x1800);
   EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
-  EXPECT_EQ(0U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
+  EXPECT_EQ(1U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
 
   regs_mips.set_pc(0x1900);
   EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
-  EXPECT_EQ(0U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
+  EXPECT_EQ(8U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
 
   regs_mips64.set_pc(0x1a00);
   EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
-  EXPECT_EQ(0U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
+  EXPECT_EQ(8U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
 }
 
-TEST_F(RegsTest, arm_set_from_raw) {
+TEST_F(RegsTest, arm_verify_sp_pc) {
   RegsArm arm;
   uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
   regs[13] = 0x100;
   regs[15] = 0x200;
-  arm.SetFromRaw();
   EXPECT_EQ(0x100U, arm.sp());
   EXPECT_EQ(0x200U, arm.pc());
 }
 
-TEST_F(RegsTest, arm64_set_from_raw) {
+TEST_F(RegsTest, arm64_verify_sp_pc) {
   RegsArm64 arm64;
   uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
   regs[31] = 0xb100000000ULL;
   regs[32] = 0xc200000000ULL;
-  arm64.SetFromRaw();
   EXPECT_EQ(0xb100000000U, arm64.sp());
   EXPECT_EQ(0xc200000000U, arm64.pc());
 }
 
-TEST_F(RegsTest, x86_set_from_raw) {
+TEST_F(RegsTest, x86_verify_sp_pc) {
   RegsX86 x86;
   uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
   regs[4] = 0x23450000;
   regs[8] = 0xabcd0000;
-  x86.SetFromRaw();
   EXPECT_EQ(0x23450000U, x86.sp());
   EXPECT_EQ(0xabcd0000U, x86.pc());
 }
 
-TEST_F(RegsTest, x86_64_set_from_raw) {
+TEST_F(RegsTest, x86_64_verify_sp_pc) {
   RegsX86_64 x86_64;
   uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
   regs[7] = 0x1200000000ULL;
   regs[16] = 0x4900000000ULL;
-  x86_64.SetFromRaw();
   EXPECT_EQ(0x1200000000U, x86_64.sp());
   EXPECT_EQ(0x4900000000U, x86_64.pc());
 }
 
-TEST_F(RegsTest, mips_set_from_raw) {
+TEST_F(RegsTest, mips_verify_sp_pc) {
   RegsMips mips;
   uint32_t* regs = reinterpret_cast<uint32_t*>(mips.RawData());
   regs[29] = 0x100;
   regs[32] = 0x200;
-  mips.SetFromRaw();
   EXPECT_EQ(0x100U, mips.sp());
   EXPECT_EQ(0x200U, mips.pc());
 }
 
-TEST_F(RegsTest, mips64_set_from_raw) {
+TEST_F(RegsTest, mips64_verify_sp_pc) {
   RegsMips64 mips64;
   uint64_t* regs = reinterpret_cast<uint64_t*>(mips64.RawData());
   regs[29] = 0xb100000000ULL;
   regs[32] = 0xc200000000ULL;
-  mips64.SetFromRaw();
   EXPECT_EQ(0xb100000000U, mips64.sp());
   EXPECT_EQ(0xc200000000U, mips64.pc());
 }
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index af4a5b5..2b8f0c2 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/mman.h>
 #include <unistd.h>
 
 #include <gtest/gtest.h>
@@ -122,7 +123,6 @@
       (*regs)[entry->second] = value;
     }
     fclose(fp);
-    regs->SetFromRaw();
   }
 
   static std::unordered_map<std::string, uint32_t> arm_regs_;
@@ -188,7 +188,7 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 0001a9f8  libc.so (abort+63)\n"
+      "  #00 pc 0001a9f8  libc.so (abort+64)\n"
       "  #01 pc 00006a1b  libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
       "  #02 pc 00007441  libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
       "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
@@ -285,7 +285,7 @@
       "  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)\n"
       "  #02 pc 000021a8 (offset 0x2000)  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+136)\n"
-      "  #03 pc 0000fe81  anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n"
+      "  #03 pc 0000fe80  anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
       "  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #05 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -300,7 +300,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #09 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #10 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #11 pc 0000fe04  anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n"
+      "  #11 pc 0000fe03  anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
       "  #12 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #13 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -315,8 +315,8 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #17 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #18 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #19 pc 0000fd3c  anonymous:ee74c000 (int Main.compare(java.lang.Object, "
-      "java.lang.Object)+108)\n"
+      "  #19 pc 0000fd3b  anonymous:ee74c000 (int Main.compare(java.lang.Object, "
+      "java.lang.Object)+107)\n"
       "  #20 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #21 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -331,9 +331,9 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #25 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #26 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #27 pc 0000fbdc  anonymous:ee74c000 (int "
+      "  #27 pc 0000fbdb  anonymous:ee74c000 (int "
       "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
-      "java.util.Comparator)+332)\n"
+      "java.util.Comparator)+331)\n"
       "  #28 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
       "  #29 pc 00146acb  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
@@ -348,7 +348,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #33 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #34 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #35 pc 0000f625  anonymous:ee74c000 (boolean Main.foo()+165)\n"
+      "  #35 pc 0000f624  anonymous:ee74c000 (boolean Main.foo()+164)\n"
       "  #36 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #37 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -363,7 +363,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #41 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #42 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #43 pc 0000eedc  anonymous:ee74c000 (void Main.runPrimary()+60)\n"
+      "  #43 pc 0000eedb  anonymous:ee74c000 (void Main.runPrimary()+59)\n"
       "  #44 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #45 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -378,7 +378,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #49 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #50 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #51 pc 0000ac22  anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n"
+      "  #51 pc 0000ac21  anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
       "  #52 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
       "  #53 pc 00146acb  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
@@ -420,7 +420,7 @@
   EXPECT_EQ(0xffeb52a0U, unwinder.frames()[1].sp);
   EXPECT_EQ(0xec6061a8U, unwinder.frames()[2].pc);
   EXPECT_EQ(0xffeb5ce0U, unwinder.frames()[2].sp);
-  EXPECT_EQ(0xee75be81U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xee75be80U, unwinder.frames()[3].pc);
   EXPECT_EQ(0xffeb5d30U, unwinder.frames()[3].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[4].pc);
   EXPECT_EQ(0xffeb5d60U, unwinder.frames()[4].sp);
@@ -436,7 +436,7 @@
   EXPECT_EQ(0xffeb5fb0U, unwinder.frames()[9].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[10].pc);
   EXPECT_EQ(0xffeb6110U, unwinder.frames()[10].sp);
-  EXPECT_EQ(0xee75be04U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xee75be03U, unwinder.frames()[11].pc);
   EXPECT_EQ(0xffeb6160U, unwinder.frames()[11].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[12].pc);
   EXPECT_EQ(0xffeb6180U, unwinder.frames()[12].sp);
@@ -452,7 +452,7 @@
   EXPECT_EQ(0xffeb63e0U, unwinder.frames()[17].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[18].pc);
   EXPECT_EQ(0xffeb6530U, unwinder.frames()[18].sp);
-  EXPECT_EQ(0xee75bd3cU, unwinder.frames()[19].pc);
+  EXPECT_EQ(0xee75bd3bU, unwinder.frames()[19].pc);
   EXPECT_EQ(0xffeb6580U, unwinder.frames()[19].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[20].pc);
   EXPECT_EQ(0xffeb65b0U, unwinder.frames()[20].sp);
@@ -468,7 +468,7 @@
   EXPECT_EQ(0xffeb6810U, unwinder.frames()[25].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[26].pc);
   EXPECT_EQ(0xffeb6960U, unwinder.frames()[26].sp);
-  EXPECT_EQ(0xee75bbdcU, unwinder.frames()[27].pc);
+  EXPECT_EQ(0xee75bbdbU, unwinder.frames()[27].pc);
   EXPECT_EQ(0xffeb69b0U, unwinder.frames()[27].sp);
   EXPECT_EQ(0xf728e6a2U, unwinder.frames()[28].pc);
   EXPECT_EQ(0xffeb69f0U, unwinder.frames()[28].sp);
@@ -484,7 +484,7 @@
   EXPECT_EQ(0xffeb6c50U, unwinder.frames()[33].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[34].pc);
   EXPECT_EQ(0xffeb6dd0U, unwinder.frames()[34].sp);
-  EXPECT_EQ(0xee75b625U, unwinder.frames()[35].pc);
+  EXPECT_EQ(0xee75b624U, unwinder.frames()[35].pc);
   EXPECT_EQ(0xffeb6e20U, unwinder.frames()[35].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[36].pc);
   EXPECT_EQ(0xffeb6e50U, unwinder.frames()[36].sp);
@@ -500,7 +500,7 @@
   EXPECT_EQ(0xffeb70a0U, unwinder.frames()[41].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[42].pc);
   EXPECT_EQ(0xffeb71f0U, unwinder.frames()[42].sp);
-  EXPECT_EQ(0xee75aedcU, unwinder.frames()[43].pc);
+  EXPECT_EQ(0xee75aedbU, unwinder.frames()[43].pc);
   EXPECT_EQ(0xffeb7240U, unwinder.frames()[43].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[44].pc);
   EXPECT_EQ(0xffeb72a0U, unwinder.frames()[44].sp);
@@ -516,7 +516,7 @@
   EXPECT_EQ(0xffeb74f0U, unwinder.frames()[49].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[50].pc);
   EXPECT_EQ(0xffeb7680U, unwinder.frames()[50].sp);
-  EXPECT_EQ(0xee756c22U, unwinder.frames()[51].pc);
+  EXPECT_EQ(0xee756c21U, unwinder.frames()[51].pc);
   EXPECT_EQ(0xffeb76d0U, unwinder.frames()[51].sp);
   EXPECT_EQ(0xf728e6a2U, unwinder.frames()[52].pc);
   EXPECT_EQ(0xffeb76f0U, unwinder.frames()[52].sp);
@@ -575,7 +575,7 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+865)\n"
+      "  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+866)\n"
       "  #01 pc 0000212d (offset 0x2000)  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+92)\n"
       "  #02 pc 00011cb1  anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
@@ -956,4 +956,159 @@
   EXPECT_EQ(0x7ffcc85971a0U, unwinder.frames()[4].sp);
 }
 
+TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
+  Init("art_quick_osr_stub_arm/", ARCH_ARM);
+
+  MemoryOfflineParts* memory = new MemoryOfflineParts;
+  AddMemory(dir_ + "descriptor.data", memory);
+  AddMemory(dir_ + "stack.data", memory);
+  for (size_t i = 0; i < 2; i++) {
+    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
+  }
+  process_memory_.reset(memory);
+
+  JitDebug jit_debug(process_memory_);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(25U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0000c788  <anonymous:d0250000> "
+      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
+      "  #01 pc 0000cdd5  <anonymous:d0250000> "
+      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
+      "  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)\n"
+      "  #03 pc 002657a5  libart.so "
+      "(_ZN3art3jit3Jit25MaybeDoOnStackReplacementEPNS_6ThreadEPNS_9ArtMethodEjiPNS_6JValueE+876)\n"
+      "  #04 pc 004021a7  libart.so (MterpMaybeDoOnStackReplacement+86)\n"
+      "  #05 pc 00412474  libart.so (ExecuteMterpImpl+66164)\n"
+      "  #06 pc cd8365b0  <unknown>\n"  // symbol in dex file
+      "  #07 pc 001d7f1b  libart.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+374)\n"
+      "  #08 pc 001dc593  libart.so "
+      "(_ZN3art11interpreter33ArtInterpreterToInterpreterBridgeEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameEPNS_6JValueE+154)\n"
+      "  #09 pc 001f4d01  libart.so "
+      "(_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_"
+      "11InstructionEtPNS_6JValueE+732)\n"
+      "  #10 pc 003fe427  libart.so (MterpInvokeInterface+1354)\n"
+      "  #11 pc 00405b94  libart.so (ExecuteMterpImpl+14740)\n"
+      "  #12 pc 7004873e  <unknown>\n"  // symbol in dex file
+      "  #13 pc 001d7f1b  libart.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+374)\n"
+      "  #14 pc 001dc4d5  libart.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+92)\n"
+      "  #15 pc 003f25ab  libart.so (artQuickToInterpreterBridge+970)\n"
+      "  #16 pc 00417aff  libart.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #17 pc 00413575  libart.so (art_quick_invoke_stub_internal+68)\n"
+      "  #18 pc 00418531  libart.so (art_quick_invoke_stub+236)\n"
+      "  #19 pc 000b468d  libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+136)\n"
+      "  #20 pc 00362f49  libart.so "
+      "(_ZN3art12_GLOBAL__N_118InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_"
+      "9ArtMethodEPNS0_8ArgArrayEPNS_6JValueEPKc+52)\n"
+      "  #21 pc 00363cd9  libart.so "
+      "(_ZN3art35InvokeVirtualOrInterfaceWithJValuesERKNS_33ScopedObjectAccessAlreadyRunnableEP8_"
+      "jobjectP10_jmethodIDP6jvalue+332)\n"
+      "  #22 pc 003851dd  libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
+      "  #23 pc 00062925  libc.so (_ZL15__pthread_startPv+22)\n"
+      "  #24 pc 0001de39  libc.so (__start_thread+24)\n",
+      frame_info);
+  EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xcd4ff140U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xe4a755bbU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xcd4ff160U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xe48c77a5U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xcd4ff190U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xe4a641a7U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xcd4ff298U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0xe4a74474U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xcd4ff2b8U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xcd8365b0U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xcd4ff2e0U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0xe4839f1bU, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xcd4ff2e0U, unwinder.frames()[7].sp);
+  EXPECT_EQ(0xe483e593U, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xcd4ff330U, unwinder.frames()[8].sp);
+  EXPECT_EQ(0xe4856d01U, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xcd4ff380U, unwinder.frames()[9].sp);
+  EXPECT_EQ(0xe4a60427U, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xcd4ff430U, unwinder.frames()[10].sp);
+  EXPECT_EQ(0xe4a67b94U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xcd4ff498U, unwinder.frames()[11].sp);
+  EXPECT_EQ(0x7004873eU, unwinder.frames()[12].pc);
+  EXPECT_EQ(0xcd4ff4c0U, unwinder.frames()[12].sp);
+  EXPECT_EQ(0xe4839f1bU, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xcd4ff4c0U, unwinder.frames()[13].sp);
+  EXPECT_EQ(0xe483e4d5U, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xcd4ff510U, unwinder.frames()[14].sp);
+  EXPECT_EQ(0xe4a545abU, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xcd4ff538U, unwinder.frames()[15].sp);
+  EXPECT_EQ(0xe4a79affU, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xcd4ff640U, unwinder.frames()[16].sp);
+  EXPECT_EQ(0xe4a75575U, unwinder.frames()[17].pc);
+  EXPECT_EQ(0xcd4ff6b0U, unwinder.frames()[17].sp);
+  EXPECT_EQ(0xe4a7a531U, unwinder.frames()[18].pc);
+  EXPECT_EQ(0xcd4ff6e8U, unwinder.frames()[18].sp);
+  EXPECT_EQ(0xe471668dU, unwinder.frames()[19].pc);
+  EXPECT_EQ(0xcd4ff770U, unwinder.frames()[19].sp);
+  EXPECT_EQ(0xe49c4f49U, unwinder.frames()[20].pc);
+  EXPECT_EQ(0xcd4ff7c8U, unwinder.frames()[20].sp);
+  EXPECT_EQ(0xe49c5cd9U, unwinder.frames()[21].pc);
+  EXPECT_EQ(0xcd4ff850U, unwinder.frames()[21].sp);
+  EXPECT_EQ(0xe49e71ddU, unwinder.frames()[22].pc);
+  EXPECT_EQ(0xcd4ff8e8U, unwinder.frames()[22].sp);
+  EXPECT_EQ(0xe7df3925U, unwinder.frames()[23].pc);
+  EXPECT_EQ(0xcd4ff958U, unwinder.frames()[23].sp);
+  EXPECT_EQ(0xe7daee39U, unwinder.frames()[24].pc);
+  EXPECT_EQ(0xcd4ff960U, unwinder.frames()[24].sp);
+}
+
+TEST_F(UnwindOfflineTest, jit_map_arm) {
+  Init("jit_map_arm/", ARCH_ARM);
+
+  maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+             "jit_map0.so", 0);
+  maps_->Add(0xd025cd98, 0xd025cff4, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+             "jit_map1.so", 0);
+  maps_->Sort();
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000000  jit_map0.so "
+      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
+      "  #01 pc 0000003d  jit_map1.so "
+      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
+      "  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)\n"
+
+      "  #03 pc 003851dd  libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
+      "  #04 pc 00062925  libc.so (_ZL15__pthread_startPv+22)\n"
+      "  #05 pc 0001de39  libc.so (__start_thread+24)\n",
+      frame_info);
+
+  EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xcd4ff140U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xe4a755bbU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xcd4ff160U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xe49e71ddU, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xcd4ff8e8U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xe7df3925U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xcd4ff958U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0xe7daee39U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xcd4ff960U, unwinder.frames()[5].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index e44b225..2428f68 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -133,7 +133,7 @@
 };
 
 MapsFake UnwinderTest::maps_;
-RegsFake UnwinderTest::regs_(5, 0);
+RegsFake UnwinderTest::regs_(5);
 std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
 
 TEST_F(UnwinderTest, multiple_frames) {
@@ -141,8 +141,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
 
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -201,8 +201,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
 
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -221,7 +221,7 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ("", frame->map_name);
   EXPECT_EQ(0U, frame->map_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
@@ -235,7 +235,7 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ("", frame->map_name);
   EXPECT_EQ(0U, frame->map_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
@@ -249,7 +249,7 @@
   EXPECT_EQ(0x10020U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ("", frame->map_name);
   EXPECT_EQ(0U, frame->map_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
@@ -260,8 +260,8 @@
 TEST_F(UnwinderTest, non_zero_load_bias) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
 
-  regs_.FakeSetPc(0xa5500);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0xa5500);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
@@ -288,8 +288,8 @@
 TEST_F(UnwinderTest, non_zero_elf_offset) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
 
-  regs_.FakeSetPc(0xa7500);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0xa7500);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
@@ -316,8 +316,8 @@
 TEST_F(UnwinderTest, non_zero_map_offset) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
 
-  regs_.FakeSetPc(0x43000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x43000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
@@ -349,8 +349,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
 
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0x1000, 0x10000, true));
   ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
@@ -383,8 +383,8 @@
     ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
   }
 
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
 
   Unwinder unwinder(20, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
@@ -415,8 +415,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
 
-  regs_.FakeSetPc(0x20000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x20000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false));
@@ -481,8 +481,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
 
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x63000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x63000);
   ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
@@ -527,8 +527,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
 
-  regs_.FakeSetPc(0x13000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x13000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -546,8 +546,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
 
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x13000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x13000);
   ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -563,8 +563,8 @@
 TEST_F(UnwinderTest, pc_without_map) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
 
-  regs_.FakeSetPc(0x41000);
-  regs_.FakeSetSp(0x13000);
+  regs_.set_pc(0x41000);
+  regs_.set_sp(0x13000);
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
@@ -593,8 +593,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
 
   // Fake as if code called a nullptr function.
-  regs_.FakeSetPc(0);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0);
+  regs_.set_sp(0x10000);
   regs_.FakeSetReturnAddress(0x1202);
   regs_.FakeSetReturnAddressValid(true);
 
@@ -657,8 +657,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
 
   // Fake as if code called a nullptr function.
-  regs_.FakeSetPc(0);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0);
+  regs_.set_sp(0x10000);
   regs_.FakeSetReturnAddress(0x1202);
   regs_.FakeSetReturnAddressValid(true);
 
@@ -691,8 +691,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
 
   // Fake as if code called a nullptr function.
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0x43402, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -745,8 +745,8 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
 
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
@@ -805,8 +805,8 @@
 
 TEST_F(UnwinderTest, dex_pc_in_map) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0xa3400);
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
@@ -846,8 +846,8 @@
 
 TEST_F(UnwinderTest, dex_pc_not_in_map) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0x50000);
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
@@ -888,8 +888,8 @@
 TEST_F(UnwinderTest, dex_pc_multiple_frames) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
-  regs_.FakeSetPc(0x1000);
-  regs_.FakeSetSp(0x10000);
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0xa3400);
   ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
new file mode 100644
index 0000000..300646b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
new file mode 100644
index 0000000..999cb79
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
new file mode 100644
index 0000000..6aa1c82
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
new file mode 100644
index 0000000..19d7b65
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
new file mode 100644
index 0000000..edcd3e1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
new file mode 100644
index 0000000..09ba495
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
new file mode 100644
index 0000000..39c9025
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
new file mode 100644
index 0000000..55aaaf6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
@@ -0,0 +1,3 @@
+d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000>
+e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
new file mode 100644
index 0000000..0b51814
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: e814103c
+r1: 12dcf218
+r2: 1a90df75
+r3: ffffffbf
+r4: 0
+r5: 12dc0800
+r6: 12dcf218
+r7: 1a90df75
+r8: 0
+r9: dd23cc00
+r10: 1c
+r11: cd4ff16c
+ip: 0
+sp: cd4ff140
+lr: d025cdd7
+pc: d025c788
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
new file mode 100644
index 0000000..f00917b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
new file mode 100644
index 0000000..e667883
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
new file mode 100644
index 0000000..9a1d714
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libart.so b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
new file mode 100644
index 0000000..09ba495
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libc.so b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
new file mode 100644
index 0000000..39c9025
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/maps.txt b/libunwindstack/tests/files/offline/jit_map_arm/maps.txt
new file mode 100644
index 0000000..5aaec54
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/maps.txt
@@ -0,0 +1,2 @@
+e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/regs.txt b/libunwindstack/tests/files/offline/jit_map_arm/regs.txt
new file mode 100644
index 0000000..0b51814
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: e814103c
+r1: 12dcf218
+r2: 1a90df75
+r3: ffffffbf
+r4: 0
+r5: 12dc0800
+r6: 12dcf218
+r7: 1a90df75
+r8: 0
+r9: dd23cc00
+r10: 1c
+r11: cd4ff16c
+ip: 0
+sp: cd4ff140
+lr: d025cdd7
+pc: d025c788
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/stack.data b/libunwindstack/tests/files/offline/jit_map_arm/stack.data
new file mode 100644
index 0000000..fb8feeb
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index a0abcca..5a8edfd 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -53,8 +53,7 @@
       printf("  PC 0x%" PRIx64, addr + load_bias);
       uint64_t func_offset;
       uint64_t pc = addr + load_bias;
-      // This might be a thumb function, so set the low bit.
-      if (interface->GetFunctionName(pc | 1, load_bias, &name, &func_offset) && !name.empty()) {
+      if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
         printf(" <%s>", name.c_str());
       }
       printf("\n");
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 209bf9a..0d7925a 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -129,7 +129,6 @@
         "PropertyMap.cpp",
         "RefBase.cpp",
         "SharedBuffer.cpp",
-        "Static.cpp",
         "StopWatch.cpp",
         "String8.cpp",
         "String16.cpp",
@@ -160,7 +159,6 @@
 cc_library {
     name: "libutilscallstack",
     defaults: ["libutils_defaults"],
-    vendor_available: false,
 
     srcs: [
         "CallStack.cpp",
@@ -201,5 +199,3 @@
         "-Werror",
     ],
 }
-
-subdirs = ["tests"]
diff --git a/libutils/Static.cpp b/libutils/Static.cpp
deleted file mode 100644
index 3ed07a1..0000000
--- a/libutils/Static.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// All static variables go here, to control initialization and
-// destruction order in the library.
-
-namespace android {
-
-// For String8.cpp
-extern void initialize_string8();
-extern void terminate_string8();
-
-// For String16.cpp
-extern void initialize_string16();
-extern void terminate_string16();
-
-class LibUtilsFirstStatics
-{
-public:
-    LibUtilsFirstStatics()
-    {
-        initialize_string8();
-        initialize_string16();
-    }
-    
-    ~LibUtilsFirstStatics()
-    {
-        terminate_string16();
-        terminate_string8();
-    }
-};
-
-static LibUtilsFirstStatics gFirstStatics;
-int gDarwinCantLoadAllObjects = 1;
-
-}   // namespace android
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index ad335c3..84d53dd 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -24,29 +24,16 @@
 
 namespace android {
 
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char16_t* gEmptyString = NULL;
+static inline char16_t* getEmptyString() {
+    static SharedBuffer* gEmptyStringBuf = [] {
+        SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
+        char16_t* str = static_cast<char16_t*>(buf->data());
+        *str = 0;
+        return buf;
+    }();
 
-static inline char16_t* getEmptyString()
-{
     gEmptyStringBuf->acquire();
-   return gEmptyString;
-}
-
-void initialize_string16()
-{
-    SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
-    char16_t* str = (char16_t*)buf->data();
-    *str = 0;
-    gEmptyStringBuf = buf;
-    gEmptyString = str;
-}
-
-void terminate_string16()
-{
-    SharedBuffer::bufferFromData(gEmptyString)->release();
-    gEmptyStringBuf = NULL;
-    gEmptyString = NULL;
+    return static_cast<char16_t*>(gEmptyStringBuf->data());
 }
 
 // ---------------------------------------------------------------------------
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index ad0e72e..580e870 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -40,40 +40,16 @@
 // to OS_PATH_SEPARATOR.
 #define RES_PATH_SEPARATOR '/'
 
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char* gEmptyString = NULL;
+static inline char* getEmptyString() {
+    static SharedBuffer* gEmptyStringBuf = [] {
+        SharedBuffer* buf = SharedBuffer::alloc(1);
+        char* str = static_cast<char*>(buf->data());
+        *str = 0;
+        return buf;
+    }();
 
-extern int gDarwinCantLoadAllObjects;
-int gDarwinIsReallyAnnoying;
-
-void initialize_string8();
-
-static inline char* getEmptyString()
-{
     gEmptyStringBuf->acquire();
-    return gEmptyString;
-}
-
-void initialize_string8()
-{
-    // HACK: This dummy dependency forces linking libutils Static.cpp,
-    // which is needed to initialize String8/String16 classes.
-    // These variables are named for Darwin, but are needed elsewhere too,
-    // including static linking on any platform.
-    gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
-
-    SharedBuffer* buf = SharedBuffer::alloc(1);
-    char* str = (char*)buf->data();
-    *str = 0;
-    gEmptyStringBuf = buf;
-    gEmptyString = str;
-}
-
-void terminate_string8()
-{
-    SharedBuffer::bufferFromData(gEmptyString)->release();
-    gEmptyStringBuf = NULL;
-    gEmptyString = NULL;
+    return static_cast<char*>(gEmptyStringBuf->data());
 }
 
 // ---------------------------------------------------------------------------
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f9f8c73..5e5e7af 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -224,7 +224,9 @@
     return kInvalidOffset;
   }
   if (eocd->num_records == 0) {
+#if defined(__ANDROID__)
     ALOGW("Zip: empty archive?");
+#endif
     return kEmptyArchive;
   }
 
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 3f8a503..d172755 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -4,10 +4,32 @@
     srcs: ["lmkd.c"],
     shared_libs: [
         "liblog",
-        "libprocessgroup",
         "libcutils",
     ],
+    local_include_dirs: ["include"],
     cflags: ["-Werror"],
 
     init_rc: ["lmkd.rc"],
+
+    product_variables: {
+        debuggable: {
+            cflags: [
+                "-DLMKD_TRACE_KILLS"
+            ],
+        },
+    },
+}
+
+cc_library_static {
+    name: "liblmkd_utils",
+    srcs: ["liblmkd_utils.c"],
+    shared_libs: [
+        "libcutils",
+    ],
+    export_include_dirs: ["include"],
+    cppflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+    ]
 }
diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
new file mode 100644
index 0000000..72e3f4a
--- /dev/null
+++ b/lmkd/include/liblmkd_utils.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _LIBLMKD_UTILS_H_
+#define _LIBLMKD_UTILS_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <lmkd.h>
+
+__BEGIN_DECLS
+
+/*
+ * Connects to lmkd process and returns socket handle.
+ * On success returns socket handle.
+ * On error, -1 is returned, and errno is set appropriately.
+ */
+int lmkd_connect();
+
+/*
+ * Registers a process with lmkd and sets its oomadj score.
+ * On success returns 0.
+ * On error, -1 is returned.
+ * In the case of error errno is set appropriately.
+ */
+int lmkd_register_proc(int sock, struct lmk_procprio *params);
+
+/*
+ * Creates memcg directory for given process.
+ * On success returns 0.
+ * -1 is returned if path creation failed.
+ * -2 is returned if tasks file open operation failed.
+ * -3 is returned if tasks file write operation failed.
+ * In the case of error errno is set appropriately.
+ */
+int create_memcg(uid_t uid, pid_t pid);
+
+__END_DECLS
+
+#endif /* _LIBLMKD_UTILS_H_ */
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
new file mode 100644
index 0000000..fe6364d
--- /dev/null
+++ b/lmkd/include/lmkd.h
@@ -0,0 +1,147 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _LMKD_H_
+#define _LMKD_H_
+
+#include <arpa/inet.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * Supported LMKD commands
+ */
+enum lmk_cmd {
+    LMK_TARGET = 0,  /* Associate minfree with oom_adj_score */
+    LMK_PROCPRIO,    /* Register a process and set its oom_adj_score */
+    LMK_PROCREMOVE,  /* Unregister a process */
+};
+
+/*
+ * Max number of targets in LMK_TARGET command.
+ */
+#define MAX_TARGETS 6
+
+/*
+ * Max packet length in bytes.
+ * Longest packet is LMK_TARGET followed by MAX_TARGETS
+ * of minfree and oom_adj_score values
+ */
+#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* LMKD packet - first int is lmk_cmd followed by payload */
+typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
+
+/* Get LMKD packet command */
+inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+    return (enum lmk_cmd)ntohl(pack[0]);
+}
+
+/* LMK_TARGET packet payload */
+struct lmk_target {
+    int minfree;
+    int oom_adj_score;
+};
+
+/*
+ * For LMK_TARGET packet get target_idx-th payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
+                                 int target_idx, struct lmk_target *target) {
+    target->minfree = ntohl(packet[target_idx * 2 + 1]);
+    target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
+}
+
+/*
+ * Prepare LMK_TARGET packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
+                                   struct lmk_target *targets,
+                                   size_t target_cnt) {
+    int idx = 0;
+    packet[idx++] = htonl(LMK_TARGET);
+    while (target_cnt) {
+        packet[idx++] = htonl(targets->minfree);
+        packet[idx++] = htonl(targets->oom_adj_score);
+        targets++;
+        target_cnt--;
+    }
+    return idx * sizeof(int);
+}
+
+/* LMK_PROCPRIO packet payload */
+struct lmk_procprio {
+    pid_t pid;
+    uid_t uid;
+    int oomadj;
+};
+
+/*
+ * For LMK_PROCPRIO packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    params->pid = (pid_t)ntohl(packet[1]);
+    params->uid = (uid_t)ntohl(packet[2]);
+    params->oomadj = ntohl(packet[3]);
+}
+
+/*
+ * Prepare LMK_PROCPRIO packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    packet[0] = htonl(LMK_PROCPRIO);
+    packet[1] = htonl(params->pid);
+    packet[2] = htonl(params->uid);
+    packet[3] = htonl(params->oomadj);
+    return 4 * sizeof(int);
+}
+
+/* LMK_PROCREMOVE packet payload */
+struct lmk_procremove {
+    pid_t pid;
+};
+
+/*
+ * For LMK_PROCREMOVE packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procremove *params) {
+    params->pid = (pid_t)ntohl(packet[1]);
+}
+
+/*
+ * Prepare LMK_PROCREMOVE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    packet[0] = htonl(LMK_PROCREMOVE);
+    packet[1] = htonl(params->pid);
+    return 2 * sizeof(int);
+}
+
+__END_DECLS
+
+#endif /* _LMKD_H_ */
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
new file mode 100644
index 0000000..fa3b7a9
--- /dev/null
+++ b/lmkd/liblmkd_utils.c
@@ -0,0 +1,76 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <liblmkd_utils.h>
+#include <cutils/sockets.h>
+
+int lmkd_connect() {
+    return socket_local_client("lmkd",
+                               ANDROID_SOCKET_NAMESPACE_RESERVED,
+                               SOCK_SEQPACKET);
+}
+
+int lmkd_register_proc(int sock, struct lmk_procprio *params) {
+    LMKD_CTRL_PACKET packet;
+    size_t size;
+    int ret;
+
+    size = lmkd_pack_set_procprio(packet, params);
+    ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
+
+    return (ret < 0) ? -1 : 0;
+}
+
+int create_memcg(uid_t uid, pid_t pid) {
+    char buf[256];
+    int tasks_file;
+    int written;
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
+    if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+        errno != EEXIST) {
+        return -1;
+    }
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
+    if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+        errno != EEXIST) {
+        return -1;
+    }
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
+    tasks_file = open(buf, O_WRONLY);
+    if (tasks_file < 0) {
+        return -2;
+    }
+    written = snprintf(buf, sizeof(buf), "%u", pid);
+    if (__predict_false(written >= (int)sizeof(buf))) {
+        written = sizeof(buf) - 1;
+    }
+    written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
+    close(tasks_file);
+
+    return (written < 0) ? -3 : 0;
+}
+
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 15471e0..72f9f7b 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,11 +16,12 @@
 
 #define LOG_TAG "lowmemorykiller"
 
-#include <arpa/inet.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <pwd.h>
 #include <sched.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/cdefs.h>
@@ -29,13 +30,32 @@
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <time.h>
+#include <sys/sysinfo.h>
 #include <unistd.h>
 
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
+#include <lmkd.h>
 #include <log/log.h>
-#include <processgroup/processgroup.h>
+
+/*
+ * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
+ * to profile and correlate with OOM kills
+ */
+#ifdef LMKD_TRACE_KILLS
+
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <cutils/trace.h>
+
+#define TRACE_KILL_START(pid) ATRACE_INT(__FUNCTION__, pid);
+#define TRACE_KILL_END()      ATRACE_INT(__FUNCTION__, 0);
+
+#else /* LMKD_TRACE_KILLS */
+
+#define TRACE_KILL_START(pid)
+#define TRACE_KILL_END()
+
+#endif /* LMKD_TRACE_KILLS */
 
 #ifndef __unused
 #define __unused __attribute__((__unused__))
@@ -44,8 +64,6 @@
 #define MEMCG_SYSFS_PATH "/dev/memcg/"
 #define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
 #define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
-#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium"
-#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical"
 #define ZONEINFO_PATH "/proc/zoneinfo"
 #define LINE_MAX 128
 
@@ -55,43 +73,71 @@
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
 #define EIGHT_MEGA (1 << 23)
 
-enum lmk_cmd {
-    LMK_TARGET,
-    LMK_PROCPRIO,
-    LMK_PROCREMOVE,
-};
-
-#define MAX_TARGETS 6
-/*
- * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
- * values
- */
-#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
+/* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
+#define SYSTEM_ADJ (-900)
 
 /* default to old in-kernel interface if no memory pressure events */
 static int use_inkernel_interface = 1;
 static bool has_inkernel_module;
 
-/* memory pressure level medium event */
-static int mpevfd[2];
-#define CRITICAL_INDEX 1
-#define MEDIUM_INDEX 0
+/* memory pressure levels */
+enum vmpressure_level {
+    VMPRESS_LEVEL_LOW = 0,
+    VMPRESS_LEVEL_MEDIUM,
+    VMPRESS_LEVEL_CRITICAL,
+    VMPRESS_LEVEL_COUNT
+};
 
-static int medium_oomadj;
-static int critical_oomadj;
+static const char *level_name[] = {
+    "low",
+    "medium",
+    "critical"
+};
+
+struct mem_size {
+    int free_mem;
+    int free_swap;
+};
+
+struct {
+    int min_free; /* recorded but not used yet */
+    int max_free;
+} low_pressure_mem = { -1, -1 };
+
+static int level_oomadj[VMPRESS_LEVEL_COUNT];
+static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
 static bool debug_process_killing;
 static bool enable_pressure_upgrade;
 static int64_t upgrade_pressure;
 static int64_t downgrade_pressure;
 static bool is_go_device;
+static bool kill_heaviest_task;
+static unsigned long kill_timeout_ms;
 
-/* control socket listen and data */
-static int ctrl_lfd;
-static int ctrl_dfd = -1;
-static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
+/* data required to handle events */
+struct event_handler_info {
+    int data;
+    void (*handler)(int data, uint32_t events);
+};
 
-/* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 4
+/* data required to handle socket events */
+struct sock_event_handler_info {
+    int sock;
+    struct event_handler_info handler_info;
+};
+
+/* max supported number of data connections */
+#define MAX_DATA_CONN 2
+
+/* socket event handler data */
+static struct sock_event_handler_info ctrl_sock;
+static struct sock_event_handler_info data_sock[MAX_DATA_CONN];
+
+/* vmpressure event handler data */
+static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
+
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
 static int epollfd;
 static int maxevents;
 
@@ -226,65 +272,84 @@
     return 0;
 }
 
-static void writefilestring(char *path, char *s) {
+/*
+ * Write a string to a file.
+ * Returns false if the file does not exist.
+ */
+static bool writefilestring(const char *path, const char *s,
+                            bool err_if_missing) {
     int fd = open(path, O_WRONLY | O_CLOEXEC);
-    int len = strlen(s);
-    int ret;
+    ssize_t len = strlen(s);
+    ssize_t ret;
 
     if (fd < 0) {
-        ALOGE("Error opening %s; errno=%d", path, errno);
-        return;
+        if (err_if_missing) {
+            ALOGE("Error opening %s; errno=%d", path, errno);
+        }
+        return false;
     }
 
-    ret = write(fd, s, len);
+    ret = TEMP_FAILURE_RETRY(write(fd, s, len));
     if (ret < 0) {
         ALOGE("Error writing %s; errno=%d", path, errno);
     } else if (ret < len) {
-        ALOGE("Short write on %s; length=%d", path, ret);
+        ALOGE("Short write on %s; length=%zd", path, ret);
     }
 
     close(fd);
+    return true;
 }
 
-static void cmd_procprio(int pid, int uid, int oomadj) {
+static void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
     char path[80];
     char val[20];
     int soft_limit_mult;
+    struct lmk_procprio params;
+    bool is_system_server;
+    struct passwd *pwdrec;
 
-    if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
-        ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+    lmkd_pack_get_procprio(packet, &params);
+
+    if (params.oomadj < OOM_SCORE_ADJ_MIN ||
+        params.oomadj > OOM_SCORE_ADJ_MAX) {
+        ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);
         return;
     }
 
-    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
-    snprintf(val, sizeof(val), "%d", oomadj);
-    writefilestring(path, val);
+    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
+    snprintf(val, sizeof(val), "%d", params.oomadj);
+    if (!writefilestring(path, val, false)) {
+        ALOGW("Failed to open %s; errno=%d: process %d might have been killed",
+              path, errno, params.pid);
+        /* If this file does not exist the process is dead. */
+        return;
+    }
 
     if (use_inkernel_interface)
         return;
 
-    if (oomadj >= 900) {
+    if (params.oomadj >= 900) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 800) {
+    } else if (params.oomadj >= 800) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 700) {
+    } else if (params.oomadj >= 700) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 600) {
+    } else if (params.oomadj >= 600) {
         // Launcher should be perceptible, don't kill it.
-        oomadj = 200;
+        params.oomadj = 200;
         soft_limit_mult = 1;
-    } else if (oomadj >= 500) {
+    } else if (params.oomadj >= 500) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 400) {
+    } else if (params.oomadj >= 400) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 300) {
+    } else if (params.oomadj >= 300) {
         soft_limit_mult = 1;
-    } else if (oomadj >= 200) {
+    } else if (params.oomadj >= 200) {
         soft_limit_mult = 2;
-    } else if (oomadj >= 100) {
+    } else if (params.oomadj >= 100) {
         soft_limit_mult = 10;
-    } else if (oomadj >=   0) {
+    } else if (params.oomadj >=   0) {
         soft_limit_mult = 20;
     } else {
         // Persistent processes will have a large
@@ -292,11 +357,21 @@
         soft_limit_mult = 64;
     }
 
-    snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
+    snprintf(path, sizeof(path),
+             "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+             params.uid, params.pid);
     snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
-    writefilestring(path, val);
 
-    procp = pid_lookup(pid);
+    /*
+     * system_server process has no memcg under /dev/memcg/apps but should be
+     * registered with lmkd. This is the best way so far to identify it.
+     */
+    is_system_server = (params.oomadj == SYSTEM_ADJ &&
+                        (pwdrec = getpwnam("system")) != NULL &&
+                        params.uid == pwdrec->pw_uid);
+    writefilestring(path, val, !is_system_server);
+
+    procp = pid_lookup(params.pid);
     if (!procp) {
             procp = malloc(sizeof(struct proc));
             if (!procp) {
@@ -304,33 +379,38 @@
                 return;
             }
 
-            procp->pid = pid;
-            procp->uid = uid;
-            procp->oomadj = oomadj;
+            procp->pid = params.pid;
+            procp->uid = params.uid;
+            procp->oomadj = params.oomadj;
             proc_insert(procp);
     } else {
         proc_unslot(procp);
-        procp->oomadj = oomadj;
+        procp->oomadj = params.oomadj;
         proc_slot(procp);
     }
 }
 
-static void cmd_procremove(int pid) {
+static void cmd_procremove(LMKD_CTRL_PACKET packet) {
+    struct lmk_procremove params;
+
     if (use_inkernel_interface)
         return;
 
-    pid_remove(pid);
+    lmkd_pack_get_procremove(packet, &params);
+    pid_remove(params.pid);
 }
 
-static void cmd_target(int ntargets, int *params) {
+static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
+    struct lmk_target target;
 
     if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
         return;
 
     for (i = 0; i < ntargets; i++) {
-        lowmem_minfree[i] = ntohl(*params++);
-        lowmem_adj[i] = ntohl(*params++);
+        lmkd_pack_get_target(packet, i, &target);
+        lowmem_minfree[i] = target.minfree;
+        lowmem_adj[i] = target.oom_adj_score;
     }
 
     lowmem_targets_size = ntargets;
@@ -356,22 +436,29 @@
             strlcat(killpriostr, val, sizeof(killpriostr));
         }
 
-        writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
-        writefilestring(INKERNEL_ADJ_PATH, killpriostr);
+        writefilestring(INKERNEL_MINFREE_PATH, minfreestr, true);
+        writefilestring(INKERNEL_ADJ_PATH, killpriostr, true);
     }
 }
 
-static void ctrl_data_close(void) {
-    ALOGI("Closing Activity Manager data connection");
-    close(ctrl_dfd);
-    ctrl_dfd = -1;
+static void ctrl_data_close(int dsock_idx) {
+    struct epoll_event epev;
+
+    ALOGI("closing lmkd data connection");
+    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, data_sock[dsock_idx].sock, &epev) == -1) {
+        // Log a warning and keep going
+        ALOGW("epoll_ctl for data connection socket failed; errno=%d", errno);
+    }
     maxevents--;
+
+    close(data_sock[dsock_idx].sock);
+    data_sock[dsock_idx].sock = -1;
 }
 
-static int ctrl_data_read(char *buf, size_t bufsz) {
+static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
     int ret = 0;
 
-    ret = read(ctrl_dfd, buf, bufsz);
+    ret = read(data_sock[dsock_idx].sock, buf, bufsz);
 
     if (ret == -1) {
         ALOGE("control data socket read failed; errno=%d", errno);
@@ -383,39 +470,43 @@
     return ret;
 }
 
-static void ctrl_command_handler(void) {
-    int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+static void ctrl_command_handler(int dsock_idx) {
+    LMKD_CTRL_PACKET packet;
     int len;
-    int cmd = -1;
+    enum lmk_cmd cmd;
     int nargs;
     int targets;
 
-    len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+    len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
     if (len <= 0)
         return;
 
+    if (len < (int)sizeof(int)) {
+        ALOGE("Wrong control socket read length len=%d", len);
+        return;
+    }
+
+    cmd = lmkd_pack_get_cmd(packet);
     nargs = len / sizeof(int) - 1;
     if (nargs < 0)
         goto wronglen;
 
-    cmd = ntohl(ibuf[0]);
-
     switch(cmd) {
     case LMK_TARGET:
         targets = nargs / 2;
         if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
             goto wronglen;
-        cmd_target(targets, &ibuf[1]);
+        cmd_target(targets, packet);
         break;
     case LMK_PROCPRIO:
         if (nargs != 3)
             goto wronglen;
-        cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
+        cmd_procprio(packet);
         break;
     case LMK_PROCREMOVE:
         if (nargs != 1)
             goto wronglen;
-        cmd_procremove(ntohl(ibuf[1]));
+        cmd_procremove(packet);
         break;
     default:
         ALOGE("Received unknown command code %d", cmd);
@@ -428,40 +519,57 @@
     ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
 }
 
-static void ctrl_data_handler(uint32_t events) {
-    if (events & EPOLLHUP) {
-        ALOGI("ActivityManager disconnected");
-        if (!ctrl_dfd_reopened)
-            ctrl_data_close();
-    } else if (events & EPOLLIN) {
-        ctrl_command_handler();
+static void ctrl_data_handler(int data, uint32_t events) {
+    if (events & EPOLLIN) {
+        ctrl_command_handler(data);
     }
 }
 
-static void ctrl_connect_handler(uint32_t events __unused) {
-    struct epoll_event epev;
+static int get_free_dsock() {
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        if (data_sock[i].sock < 0) {
+            return i;
+        }
+    }
+    return -1;
+}
 
-    if (ctrl_dfd >= 0) {
-        ctrl_data_close();
-        ctrl_dfd_reopened = 1;
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
+    struct epoll_event epev;
+    int free_dscock_idx = get_free_dsock();
+
+    if (free_dscock_idx < 0) {
+        /*
+         * Number of data connections exceeded max supported. This should not
+         * happen but if it does we drop all existing connections and accept
+         * the new one. This prevents inactive connections from monopolizing
+         * data socket and if we drop ActivityManager connection it will
+         * immediately reconnect.
+         */
+        for (int i = 0; i < MAX_DATA_CONN; i++) {
+            ctrl_data_close(i);
+        }
+        free_dscock_idx = 0;
     }
 
-    ctrl_dfd = accept(ctrl_lfd, NULL, NULL);
-
-    if (ctrl_dfd < 0) {
+    data_sock[free_dscock_idx].sock = accept(ctrl_sock.sock, NULL, NULL);
+    if (data_sock[free_dscock_idx].sock < 0) {
         ALOGE("lmkd control socket accept failed; errno=%d", errno);
         return;
     }
 
-    ALOGI("ActivityManager connected");
-    maxevents++;
+    ALOGI("lmkd data connection established");
+    /* use data to store data connection idx */
+    data_sock[free_dscock_idx].handler_info.data = free_dscock_idx;
+    data_sock[free_dscock_idx].handler_info.handler = ctrl_data_handler;
     epev.events = EPOLLIN;
-    epev.data.ptr = (void *)ctrl_data_handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+    epev.data.ptr = (void *)&(data_sock[free_dscock_idx].handler_info);
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, data_sock[free_dscock_idx].sock, &epev) == -1) {
         ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
-        ctrl_data_close();
+        ctrl_data_close(free_dscock_idx);
         return;
     }
+    maxevents++;
 }
 
 static int zoneinfo_parse_protection(char *cp) {
@@ -534,6 +642,18 @@
     return 0;
 }
 
+static int get_free_memory(struct mem_size *ms) {
+    struct sysinfo si;
+
+    if (sysinfo(&si) < 0)
+        return -1;
+
+    ms->free_mem = (int)(si.freeram * si.mem_unit / PAGE_SIZE);
+    ms->free_swap = (int)(si.freeswap * si.mem_unit / PAGE_SIZE);
+
+    return 0;
+}
+
 static int proc_get_size(int pid) {
     char path[PATH_MAX];
     char line[LINE_MAX];
@@ -586,8 +706,32 @@
     return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
 }
 
+static struct proc *proc_get_heaviest(int oomadj) {
+    struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
+    struct adjslot_list *curr = head->next;
+    struct proc *maxprocp = NULL;
+    int maxsize = 0;
+    while (curr != head) {
+        int pid = ((struct proc *)curr)->pid;
+        int tasksize = proc_get_size(pid);
+        if (tasksize <= 0) {
+            struct adjslot_list *next = curr->next;
+            pid_remove(pid);
+            curr = next;
+        } else {
+            if (tasksize > maxsize) {
+                maxsize = tasksize;
+                maxprocp = (struct proc *)curr;
+            }
+            curr = curr->next;
+        }
+    }
+    return maxprocp;
+}
+
 /* Kill one process specified by procp.  Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_score_adj, bool is_critical) {
+static int kill_one_process(struct proc* procp, int min_score_adj,
+                            enum vmpressure_level level) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
     char *taskname;
@@ -606,14 +750,18 @@
         return -1;
     }
 
+    TRACE_KILL_START(pid);
+
+    r = kill(pid, SIGKILL);
     ALOGI(
         "Killing '%s' (%d), uid %d, adj %d\n"
-        "   to free %ldkB because system is under %s memory pressure oom_adj %d\n",
-        taskname, pid, uid, procp->oomadj, tasksize * page_k, is_critical ? "critical" : "medium",
-        min_score_adj);
-    r = kill(pid, SIGKILL);
+        "   to free %ldkB because system is under %s memory pressure (min_oom_adj=%d)\n",
+        taskname, pid, uid, procp->oomadj, tasksize * page_k,
+        level_name[level], min_score_adj);
     pid_remove(pid);
 
+    TRACE_KILL_END();
+
     if (r) {
         ALOGE("kill(%d): errno=%d", pid, errno);
         return -1;
@@ -623,31 +771,40 @@
 }
 
 /*
- * Find a process to kill based on the current (possibly estimated) free memory
- * and cached memory sizes.  Returns the size of the killed processes.
+ * Find processes to kill to free required number of pages.
+ * If pages_to_free is set to 0 only one process will be killed.
+ * Returns the size of the killed processes.
  */
-static int find_and_kill_process(bool is_critical) {
+static int find_and_kill_processes(enum vmpressure_level level,
+                                   int pages_to_free) {
     int i;
-    int killed_size = 0;
-    int min_score_adj = is_critical ? critical_oomadj : medium_oomadj;
+    int killed_size;
+    int pages_freed = 0;
+    int min_score_adj = level_oomadj[level];
 
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
 
-retry:
-        procp = proc_adj_lru(i);
+        while (true) {
+            if (is_go_device)
+                procp = proc_adj_lru(i);
+            else
+                procp = proc_get_heaviest(i);
 
-        if (procp) {
-            killed_size = kill_one_process(procp, min_score_adj, is_critical);
-            if (killed_size < 0) {
-                goto retry;
-            } else {
-                return killed_size;
+            if (!procp)
+                break;
+
+            killed_size = kill_one_process(procp, min_score_adj, level);
+            if (killed_size >= 0) {
+                pages_freed += killed_size;
+                if (pages_freed >= pages_to_free) {
+                    return pages_freed;
+                }
             }
         }
     }
 
-    return 0;
+    return pages_freed;
 }
 
 static int64_t get_memory_usage(const char* path) {
@@ -674,33 +831,119 @@
     return mem_usage;
 }
 
-static void mp_event_common(bool is_critical) {
+void record_low_pressure_levels(struct mem_size *free_mem) {
+    if (low_pressure_mem.min_free == -1 ||
+        low_pressure_mem.min_free > free_mem->free_mem) {
+        if (debug_process_killing) {
+            ALOGI("Low pressure min memory update from %d to %d",
+                low_pressure_mem.min_free, free_mem->free_mem);
+        }
+        low_pressure_mem.min_free = free_mem->free_mem;
+    }
+    /*
+     * Free memory at low vmpressure events occasionally gets spikes,
+     * possibly a stale low vmpressure event with memory already
+     * freed up (no memory pressure should have been reported).
+     * Ignore large jumps in max_free that would mess up our stats.
+     */
+    if (low_pressure_mem.max_free == -1 ||
+        (low_pressure_mem.max_free < free_mem->free_mem &&
+         free_mem->free_mem - low_pressure_mem.max_free < low_pressure_mem.max_free * 0.1)) {
+        if (debug_process_killing) {
+            ALOGI("Low pressure max memory update from %d to %d",
+                low_pressure_mem.max_free, free_mem->free_mem);
+        }
+        low_pressure_mem.max_free = free_mem->free_mem;
+    }
+}
+
+enum vmpressure_level upgrade_level(enum vmpressure_level level) {
+    return (enum vmpressure_level)((level < VMPRESS_LEVEL_CRITICAL) ?
+        level + 1 : level);
+}
+
+enum vmpressure_level downgrade_level(enum vmpressure_level level) {
+    return (enum vmpressure_level)((level > VMPRESS_LEVEL_LOW) ?
+        level - 1 : level);
+}
+
+static inline unsigned long get_time_diff_ms(struct timeval *from,
+                                             struct timeval *to) {
+    return (to->tv_sec - from->tv_sec) * 1000 +
+           (to->tv_usec - from->tv_usec) / 1000;
+}
+
+static void mp_event_common(int data, uint32_t events __unused) {
     int ret;
     unsigned long long evcount;
-    int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
     int64_t mem_usage, memsw_usage;
     int64_t mem_pressure;
+    enum vmpressure_level lvl;
+    struct mem_size free_mem;
+    static struct timeval last_report_tm;
+    static unsigned long skip_count = 0;
+    enum vmpressure_level level = (enum vmpressure_level)data;
 
-    ret = read(mpevfd[index], &evcount, sizeof(evcount));
-    if (ret < 0)
-        ALOGE("Error reading memory pressure event fd; errno=%d",
-              errno);
+    /*
+     * Check all event counters from low to critical
+     * and upgrade to the highest priority one. By reading
+     * eventfd we also reset the event counters.
+     */
+    for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
+        if (mpevfd[lvl] != -1 &&
+            read(mpevfd[lvl], &evcount, sizeof(evcount)) > 0 &&
+            evcount > 0 && lvl > level) {
+            level = lvl;
+        }
+    }
+
+    if (kill_timeout_ms) {
+        struct timeval curr_tm;
+        gettimeofday(&curr_tm, NULL);
+        if (get_time_diff_ms(&last_report_tm, &curr_tm) < kill_timeout_ms) {
+            skip_count++;
+            return;
+        }
+    }
+
+    if (skip_count > 0) {
+        if (debug_process_killing) {
+            ALOGI("%lu memory pressure events were skipped after a kill!",
+                skip_count);
+        }
+        skip_count = 0;
+    }
+
+    if (get_free_memory(&free_mem) == 0) {
+        if (level == VMPRESS_LEVEL_LOW) {
+            record_low_pressure_levels(&free_mem);
+        }
+    } else {
+        ALOGE("Failed to get free memory!");
+        return;
+    }
+
+    if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
+        /* Do not monitor this pressure level */
+        return;
+    }
 
     mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
     memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
     if (memsw_usage < 0 || mem_usage < 0) {
-        find_and_kill_process(is_critical);
-        return;
+        goto do_kill;
     }
 
     // Calculate percent for swappinness.
     mem_pressure = (mem_usage * 100) / memsw_usage;
 
-    if (enable_pressure_upgrade && !is_critical) {
+    if (enable_pressure_upgrade && level != VMPRESS_LEVEL_CRITICAL) {
         // We are swapping too much.
         if (mem_pressure < upgrade_pressure) {
-            ALOGI("Event upgraded to critical.");
-            is_critical = true;
+            level = upgrade_level(level);
+            if (debug_process_killing) {
+                ALOGI("Event upgraded to %s", level_name[level]);
+            }
         }
     }
 
@@ -708,41 +951,63 @@
     // kill any process, since enough memory is available.
     if (mem_pressure > downgrade_pressure) {
         if (debug_process_killing) {
-            ALOGI("Ignore %s memory pressure", is_critical ? "critical" : "medium");
+            ALOGI("Ignore %s memory pressure", level_name[level]);
         }
         return;
-    } else if (is_critical && mem_pressure > upgrade_pressure) {
+    } else if (level == VMPRESS_LEVEL_CRITICAL &&
+               mem_pressure > upgrade_pressure) {
         if (debug_process_killing) {
             ALOGI("Downgrade critical memory pressure");
         }
-        // Downgrade event to medium, since enough memory available.
-        is_critical = false;
+        // Downgrade event, since enough memory available.
+        level = downgrade_level(level);
     }
 
-    if (find_and_kill_process(is_critical) == 0) {
-        if (debug_process_killing) {
-            ALOGI("Nothing to kill");
+do_kill:
+    if (is_go_device) {
+        /* For Go devices kill only one task */
+        if (find_and_kill_processes(level, 0) == 0) {
+            if (debug_process_killing) {
+                ALOGI("Nothing to kill");
+            }
+        }
+    } else {
+        /* If pressure level is less than critical and enough free swap then ignore */
+        if (level < VMPRESS_LEVEL_CRITICAL && free_mem.free_swap > low_pressure_mem.max_free) {
+            if (debug_process_killing) {
+                ALOGI("Ignoring pressure since %d swap pages are available ", free_mem.free_swap);
+            }
+            return;
+        }
+
+        /* Free up enough memory to downgrate the memory pressure to low level */
+        if (free_mem.free_mem < low_pressure_mem.max_free) {
+            int pages_to_free = low_pressure_mem.max_free - free_mem.free_mem;
+            if (debug_process_killing) {
+                ALOGI("Trying to free %d pages", pages_to_free);
+            }
+            int pages_freed = find_and_kill_processes(level, pages_to_free);
+            if (pages_freed < pages_to_free) {
+                if (debug_process_killing) {
+                    ALOGI("Unable to free enough memory (pages freed=%d)",
+                        pages_freed);
+                }
+            } else {
+                gettimeofday(&last_report_tm, NULL);
+            }
         }
     }
 }
 
-static void mp_event(uint32_t events __unused) {
-    mp_event_common(false);
-}
-
-static void mp_event_critical(uint32_t events __unused) {
-    mp_event_common(true);
-}
-
-static int init_mp_common(char *levelstr, void *event_handler, bool is_critical)
-{
+static bool init_mp_common(enum vmpressure_level level) {
     int mpfd;
     int evfd;
     int evctlfd;
     char buf[256];
     struct epoll_event epev;
     int ret;
-    int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
+    int level_idx = (int)level;
+    const char *levelstr = level_name[level_idx];
 
     mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
     if (mpfd < 0) {
@@ -768,7 +1033,7 @@
         goto err;
     }
 
-    ret = write(evctlfd, buf, strlen(buf) + 1);
+    ret = TEMP_FAILURE_RETRY(write(evctlfd, buf, strlen(buf) + 1));
     if (ret == -1) {
         ALOGE("cgroup.event_control write failed for level %s; errno=%d",
               levelstr, errno);
@@ -776,15 +1041,19 @@
     }
 
     epev.events = EPOLLIN;
-    epev.data.ptr = event_handler;
+    /* use data to store event level */
+    vmpressure_hinfo[level_idx].data = level_idx;
+    vmpressure_hinfo[level_idx].handler = mp_event_common;
+    epev.data.ptr = (void *)&vmpressure_hinfo[level_idx];
     ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
     if (ret == -1) {
         ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
         goto err;
     }
     maxevents++;
-    mpevfd[mpevfd_index] = evfd;
-    return 0;
+    mpevfd[level] = evfd;
+    close(evctlfd);
+    return true;
 
 err:
     close(evfd);
@@ -793,17 +1062,7 @@
 err_open_evctlfd:
     close(mpfd);
 err_open_mpfd:
-    return -1;
-}
-
-static int init_mp_medium()
-{
-    return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false);
-}
-
-static int init_mp_critical()
-{
-    return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true);
+    return false;
 }
 
 static int init(void) {
@@ -822,36 +1081,44 @@
         return -1;
     }
 
-    ctrl_lfd = android_get_control_socket("lmkd");
-    if (ctrl_lfd < 0) {
+    // mark data connections as not connected
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        data_sock[i].sock = -1;
+    }
+
+    ctrl_sock.sock = android_get_control_socket("lmkd");
+    if (ctrl_sock.sock < 0) {
         ALOGE("get lmkd control socket failed");
         return -1;
     }
 
-    ret = listen(ctrl_lfd, 1);
+    ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
     if (ret < 0) {
         ALOGE("lmkd control socket listen failed (errno=%d)", errno);
         return -1;
     }
 
     epev.events = EPOLLIN;
-    epev.data.ptr = (void *)ctrl_connect_handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
+    ctrl_sock.handler_info.handler = ctrl_connect_handler;
+    epev.data.ptr = (void *)&(ctrl_sock.handler_info);
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
         ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
         return -1;
     }
     maxevents++;
 
     has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
-    use_inkernel_interface = has_inkernel_module && !is_go_device;
+    use_inkernel_interface = has_inkernel_module;
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
     } else {
-        ret = init_mp_medium();
-        ret |= init_mp_critical();
-        if (ret)
+        if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+            !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
+            !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+            return -1;
+        }
     }
 
     for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
@@ -863,12 +1130,14 @@
 }
 
 static void mainloop(void) {
+    struct event_handler_info* handler_info;
+    struct epoll_event *evt;
+
     while (1) {
         struct epoll_event events[maxevents];
         int nevents;
         int i;
 
-        ctrl_dfd_reopened = 0;
         nevents = epoll_wait(epollfd, events, maxevents, -1);
 
         if (nevents == -1) {
@@ -878,11 +1147,33 @@
             continue;
         }
 
-        for (i = 0; i < nevents; ++i) {
-            if (events[i].events & EPOLLERR)
+        /*
+         * First pass to see if any data socket connections were dropped.
+         * Dropped connection should be handled before any other events
+         * to deallocate data connection and correctly handle cases when
+         * connection gets dropped and reestablished in the same epoll cycle.
+         * In such cases it's essential to handle connection closures first.
+         */
+        for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+            if ((evt->events & EPOLLHUP) && evt->data.ptr) {
+                ALOGI("lmkd data connection dropped");
+                handler_info = (struct event_handler_info*)evt->data.ptr;
+                ctrl_data_close(handler_info->data);
+            }
+        }
+
+        /* Second pass to handle all other events */
+        for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+            if (evt->events & EPOLLERR)
                 ALOGD("EPOLLERR on event #%d", i);
-            if (events[i].data.ptr)
-                (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+            if (evt->events & EPOLLHUP) {
+                /* This case was handled in the first pass */
+                continue;
+            }
+            if (evt->data.ptr) {
+                handler_info = (struct event_handler_info*)evt->data.ptr;
+                handler_info->handler(handler_info->data, evt->events);
+            }
         }
     }
 }
@@ -892,13 +1183,27 @@
             .sched_priority = 1,
     };
 
-    medium_oomadj = property_get_int32("ro.lmk.medium", 800);
-    critical_oomadj = property_get_int32("ro.lmk.critical", 0);
+    /* By default disable low level vmpressure events */
+    level_oomadj[VMPRESS_LEVEL_LOW] =
+        property_get_int32("ro.lmk.low", OOM_SCORE_ADJ_MAX + 1);
+    level_oomadj[VMPRESS_LEVEL_MEDIUM] =
+        property_get_int32("ro.lmk.medium", 800);
+    level_oomadj[VMPRESS_LEVEL_CRITICAL] =
+        property_get_int32("ro.lmk.critical", 0);
     debug_process_killing = property_get_bool("ro.lmk.debug", false);
-    enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false);
-    upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50);
-    downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);
+
+    /* By default disable upgrade/downgrade logic */
+    enable_pressure_upgrade =
+        property_get_bool("ro.lmk.critical_upgrade", false);
+    upgrade_pressure =
+        (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 100);
+    downgrade_pressure =
+        (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 100);
+    kill_heaviest_task =
+        property_get_bool("ro.lmk.kill_heaviest_task", true);
     is_go_device = property_get_bool("ro.config.low_ram", false);
+    kill_timeout_ms =
+        (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
 
     // MCL_ONFAULT pins pages as they fault instead of loading
     // everything immediately all at once. (Which would be bad,
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
new file mode 100644
index 0000000..cbf44e9
--- /dev/null
+++ b/lmkd/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "lmkd_unit_test",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    static_libs: [
+        "liblmkd_utils",
+    ],
+
+    target: {
+        android: {
+            srcs: ["lmkd_test.cpp"],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+
+    compile_multilib: "first",
+}
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
new file mode 100644
index 0000000..f17512d
--- /dev/null
+++ b/lmkd/tests/lmkd_test.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <lmkd.h>
+#include <liblmkd_utils.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+
+using namespace android::base;
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define LMKDTEST_RESPAWN_FLAG "LMKDTEST_RESPAWN"
+
+#define LMKD_LOGCAT_MARKER "lowmemorykiller"
+#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Killing '%s'"
+#define OOM_MARKER "Out of memory"
+#define OOM_KILL_MARKER "Killed process"
+#define MIN_LOG_SIZE 100
+
+#define ONE_MB (1 << 20)
+
+/* Test constant parameters */
+#define OOM_ADJ_MAX 1000
+#define OOM_ADJ_MIN 0
+#define OOM_ADJ_STEP 100
+#define STEP_COUNT ((OOM_ADJ_MAX - OOM_ADJ_MIN) / OOM_ADJ_STEP + 1)
+
+#define ALLOC_STEP (ONE_MB)
+#define ALLOC_DELAY 1000
+
+/* Utility functions */
+std::string readCommand(const std::string& command) {
+    FILE* fp = popen(command.c_str(), "r");
+    std::string content;
+    ReadFdToString(fileno(fp), &content);
+    pclose(fp);
+    return content;
+}
+
+std::string readLogcat(const std::string& marker) {
+    std::string content = readCommand("logcat -d -b all");
+    size_t pos = content.find(marker);
+    if (pos == std::string::npos) return "";
+    content.erase(0, pos);
+    return content;
+}
+
+bool writeFile(const std::string& file, const std::string& string) {
+    if (getuid() == static_cast<unsigned>(AID_ROOT)) {
+        return WriteStringToFile(string, file);
+    }
+    return string == readCommand(
+        "echo -n '" + string + "' | su root tee " + file + " 2>&1");
+}
+
+bool writeKmsg(const std::string& marker) {
+    return writeFile("/dev/kmsg", marker);
+}
+
+std::string getTextAround(const std::string& text, size_t pos,
+                          size_t lines_before, size_t lines_after) {
+    size_t start_pos = pos;
+
+    // find start position
+    // move up lines_before number of lines
+    while (lines_before > 0 &&
+           (start_pos = text.rfind('\n', start_pos)) != std::string::npos) {
+        lines_before--;
+    }
+    // move to the beginning of the line
+    start_pos = text.rfind('\n', start_pos);
+    start_pos = (start_pos == std::string::npos) ? 0 : start_pos + 1;
+
+    // find end position
+    // move down lines_after number of lines
+    while (lines_after > 0 &&
+           (pos = text.find('\n', pos)) != std::string::npos) {
+        pos++;
+        lines_after--;
+    }
+    return text.substr(start_pos, (pos == std::string::npos) ?
+                       std::string::npos : pos - start_pos);
+}
+
+bool getExecPath(std::string &path) {
+    // exec path as utf8z c_str().
+    // std::string contains _all_ nul terminated argv[] strings.
+    return android::base::ReadFileToString("/proc/self/cmdline", &path);
+}
+
+/* Child synchronization primitives */
+#define STATE_INIT 0
+#define STATE_CHILD_READY 1
+#define STATE_PARENT_READY 2
+
+struct state_sync {
+    pthread_mutex_t mutex;
+    pthread_cond_t condition;
+    int state;
+};
+
+struct state_sync * init_state_sync_obj() {
+    struct state_sync *ssync;
+
+    ssync = (struct state_sync*)mmap(NULL, sizeof(struct state_sync),
+                PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+    if (ssync == MAP_FAILED) {
+        return NULL;
+    }
+
+    pthread_mutexattr_t mattr;
+    pthread_mutexattr_init(&mattr);
+    pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+    pthread_mutex_init(&ssync->mutex, &mattr);
+
+    pthread_condattr_t cattr;
+    pthread_condattr_init(&cattr);
+    pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+    pthread_cond_init(&ssync->condition, &cattr);
+
+    ssync->state = STATE_INIT;
+    return ssync;
+}
+
+void destroy_state_sync_obj(struct state_sync *ssync) {
+    pthread_cond_destroy(&ssync->condition);
+    pthread_mutex_destroy(&ssync->mutex);
+    munmap(ssync, sizeof(struct state_sync));
+}
+
+void signal_state(struct state_sync *ssync, int state) {
+    pthread_mutex_lock(&ssync->mutex);
+    ssync->state = state;
+    pthread_cond_signal(&ssync->condition);
+    pthread_mutex_unlock(&ssync->mutex);
+}
+
+void wait_for_state(struct state_sync *ssync, int state) {
+    pthread_mutex_lock(&ssync->mutex);
+    while (ssync->state != state) {
+        pthread_cond_wait(&ssync->condition, &ssync->mutex);
+    }
+    pthread_mutex_unlock(&ssync->mutex);
+}
+
+/* Memory allocation and data sharing */
+struct shared_data {
+    size_t allocated;
+    bool finished;
+    size_t total_size;
+    size_t step_size;
+    size_t step_delay;
+    int oomadj;
+};
+
+volatile void *gptr;
+void add_pressure(struct shared_data *data) {
+    volatile void *ptr;
+    size_t allocated_size = 0;
+
+    data->finished = false;
+    while (allocated_size < data->total_size) {
+        ptr = mmap(NULL, data->step_size, PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+        if (ptr != MAP_FAILED) {
+            /* create ptr aliasing to prevent compiler optimizing the access */
+            gptr = ptr;
+            /* make data non-zero */
+            memset((void*)ptr, (int)(allocated_size + 1), data->step_size);
+            allocated_size += data->step_size;
+            data->allocated = allocated_size;
+        }
+        usleep(data->step_delay);
+    }
+    data->finished = (allocated_size >= data->total_size);
+}
+
+/* Memory stress test main body */
+void runMemStressTest() {
+    struct shared_data *data;
+    struct state_sync *ssync;
+    int sock;
+    pid_t pid;
+    uid_t uid = getuid();
+
+    ASSERT_FALSE((sock = lmkd_connect()) < 0)
+        << "Failed to connect to lmkd process, err=" << strerror(errno);
+
+    /* allocate shared memory to communicate params with a child */
+    data = (struct shared_data*)mmap(NULL, sizeof(struct shared_data),
+                PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+    ASSERT_FALSE(data == MAP_FAILED) << "Memory allocation failure";
+    data->total_size = (size_t)-1; /* allocate until killed */
+    data->step_size = ALLOC_STEP;
+    data->step_delay = ALLOC_DELAY;
+
+    /* allocate state sync object */
+    ASSERT_FALSE((ssync = init_state_sync_obj()) == NULL)
+        << "Memory allocation failure";
+
+    /* run the test gradually decreasing oomadj */
+    data->oomadj = OOM_ADJ_MAX;
+    while (data->oomadj >= OOM_ADJ_MIN) {
+        ASSERT_FALSE((pid = fork()) < 0)
+            << "Failed to spawn a child process, err=" << strerror(errno);
+        if (pid != 0) {
+            /* Parent */
+            struct lmk_procprio params;
+            /* wait for child to start and get ready */
+            wait_for_state(ssync, STATE_CHILD_READY);
+            params.pid = pid;
+            params.uid = uid;
+            params.oomadj = data->oomadj;
+            ASSERT_FALSE(lmkd_register_proc(sock, &params) < 0)
+                << "Failed to communicate with lmkd, err=" << strerror(errno);
+            // signal the child it can proceed
+            signal_state(ssync, STATE_PARENT_READY);
+            waitpid(pid, NULL, 0);
+            if (data->finished) {
+                GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+                                 << data->allocated / ONE_MB << "MB";
+            } else {
+                GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+                                 << data->allocated / ONE_MB
+                                 << "MB before being killed";
+            }
+            data->oomadj -= OOM_ADJ_STEP;
+        } else {
+            /* Child */
+            pid = getpid();
+            GTEST_LOG_(INFO) << "Child [pid=" << pid
+                             << "] is running at oomadj="
+                             << data->oomadj;
+            data->allocated = 0;
+            data->finished = false;
+            ASSERT_FALSE(create_memcg(uid, pid) != 0)
+                << "Child [pid=" << pid << "] failed to create a cgroup";
+            signal_state(ssync, STATE_CHILD_READY);
+            wait_for_state(ssync, STATE_PARENT_READY);
+            add_pressure(data);
+            /* should not reach here, child should be killed by OOM/LMK */
+            FAIL() << "Child [pid=" << pid << "] was not killed";
+            break;
+        }
+    }
+    destroy_state_sync_obj(ssync);
+    munmap(data, sizeof(struct shared_data));
+    close(sock);
+}
+
+TEST(lmkd, check_for_oom) {
+    // test requirements
+    //   userdebug build
+    if (!__android_log_is_debuggable()) {
+        GTEST_LOG_(INFO) << "Must be userdebug build, terminating test";
+        return;
+    }
+    // check if in-kernel LMK driver is present
+    if (!access(INKERNEL_MINFREE_PATH, W_OK)) {
+        GTEST_LOG_(INFO) << "Must not have kernel lowmemorykiller driver,"
+                         << " terminating test";
+        return;
+    }
+
+    // if respawned test process then run the test and exit (no analysis)
+    if (getenv(LMKDTEST_RESPAWN_FLAG) != NULL) {
+        runMemStressTest();
+        return;
+    }
+
+    // Main test process
+    // mark the beginning of the test
+    std::string marker = StringPrintf(
+        "LMKD test start %lu\n", static_cast<unsigned long>(time(nullptr)));
+    ASSERT_TRUE(writeKmsg(marker));
+
+    // get executable complete path
+    std::string test_path;
+    ASSERT_TRUE(getExecPath(test_path));
+
+    std::string test_output;
+    if (getuid() != static_cast<unsigned>(AID_ROOT)) {
+        // if not root respawn itself as root and capture output
+        std::string command = StringPrintf(
+            "%s=true su root %s --gtest_filter=lmkd.check_for_oom 2>&1",
+            LMKDTEST_RESPAWN_FLAG, test_path.c_str());
+        std::string test_output = readCommand(command);
+        GTEST_LOG_(INFO) << test_output;
+    } else {
+        // main test process is root, run the test
+        runMemStressTest();
+    }
+
+    // Analyze results
+    // capture logcat containind kernel logs
+    std::string logcat_out = readLogcat(marker);
+
+    // 1. extract LMKD kills from logcat output, count kills
+    std::stringstream kill_logs;
+    int hit_count = 0;
+    size_t pos = 0;
+    marker = StringPrintf(LMKD_KILL_MARKER_TEMPLATE, test_path.c_str());
+
+    while (true) {
+        if ((pos = logcat_out.find(marker, pos)) != std::string::npos) {
+            kill_logs << getTextAround(logcat_out, pos, 0, 1);
+            pos += marker.length();
+            hit_count++;
+        } else {
+            break;
+        }
+    }
+    GTEST_LOG_(INFO) << "====Logged kills====" << std::endl
+                     << kill_logs.str();
+    EXPECT_TRUE(hit_count == STEP_COUNT) << "Number of kills " << hit_count
+                                         << " is less than expected "
+                                         << STEP_COUNT;
+
+    // 2. check kernel logs for OOM kills
+    pos = logcat_out.find(OOM_MARKER);
+    bool oom_detected = (pos != std::string::npos);
+    bool oom_kill_detected = (oom_detected &&
+        logcat_out.find(OOM_KILL_MARKER, pos) != std::string::npos);
+
+    EXPECT_FALSE(oom_kill_detected) << "OOM kill is detected!";
+    if (oom_detected || oom_kill_detected) {
+        // capture logcat with logs around all OOMs
+        pos = 0;
+        while ((pos = logcat_out.find(OOM_MARKER, pos)) != std::string::npos) {
+            GTEST_LOG_(INFO) << "====Logs around OOM====" << std::endl
+                             << getTextAround(logcat_out, pos,
+                                    MIN_LOG_SIZE / 2, MIN_LOG_SIZE / 2);
+            pos += strlen(OOM_MARKER);
+        }
+    }
+
+    // output complete logcat with kernel (might get truncated)
+    GTEST_LOG_(INFO) << "====Complete logcat output====" << std::endl
+                     << logcat_out;
+}
+
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 560f490..a78319f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -171,7 +171,9 @@
     }
 
     // audit message (except sequence number) identical?
-    if (last->isBinary()) {
+    if (last->isBinary() &&
+        (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
+        (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
         if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
                                             sizeof(int32_t))) {
             return DIFFERENT;
@@ -198,7 +200,7 @@
 
 int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
                    pid_t tid, const char* msg, unsigned short len) {
-    if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
+    if (log_id >= LOG_ID_MAX) {
         return -EINVAL;
     }
 
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
index 8661d7d..92e1e27 100644
--- a/mkbootimg/Android.mk
+++ b/mkbootimg/Android.mk
@@ -9,3 +9,12 @@
 LOCAL_MODULE := mkbootimg
 
 include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := unpack_bootimg
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
+
+LOCAL_MODULE := unpack_bootimg
+
+include $(BUILD_PREBUILT)
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
new file mode 100644
index 0000000..39448cf
--- /dev/null
+++ b/mkbootimg/OWNERS
@@ -0,0 +1 @@
+tbao@google.com
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 60834fe..1be8c22 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -20,16 +20,18 @@
 #ifndef _BOOT_IMAGE_H_
 #define _BOOT_IMAGE_H_
 
-typedef struct boot_img_hdr boot_img_hdr;
-
 #define BOOT_MAGIC "ANDROID!"
 #define BOOT_MAGIC_SIZE 8
 #define BOOT_NAME_SIZE 16
 #define BOOT_ARGS_SIZE 512
 #define BOOT_EXTRA_ARGS_SIZE 1024
 
-struct boot_img_hdr
-{
+#define BOOT_HEADER_VERSION_ZERO 0
+/*
+ *  Bootloader expects the structure of boot_img_hdr with header version
+ *  BOOT_HEADER_VERSION_ZERO to be as follows:
+ */
+struct boot_img_hdr_v0 {
     uint8_t magic[BOOT_MAGIC_SIZE];
 
     uint32_t kernel_size;  /* size in bytes */
@@ -43,7 +45,10 @@
 
     uint32_t tags_addr;    /* physical addr for kernel tags */
     uint32_t page_size;    /* flash page size we assume */
-    uint32_t unused;       /* reserved for future expansion: MUST be 0 */
+    /*
+     * version for the boot image header.
+     */
+    uint32_t header_version;
 
     /* operating system version and security patch level; for
      * version "A.B.C" and patch level "Y-M-D":
@@ -64,31 +69,79 @@
 } __attribute__((packed));
 
 /*
-** +-----------------+ 
-** | boot header     | 1 page
-** +-----------------+
-** | kernel          | n pages  
-** +-----------------+
-** | ramdisk         | m pages  
-** +-----------------+
-** | second stage    | o pages
-** +-----------------+
-**
-** n = (kernel_size + page_size - 1) / page_size
-** m = (ramdisk_size + page_size - 1) / page_size
-** o = (second_size + page_size - 1) / page_size
-**
-** 0. all entities are page_size aligned in flash
-** 1. kernel and ramdisk are required (size != 0)
-** 2. second is optional (second_size == 0 -> no second)
-** 3. load each element (kernel, ramdisk, second) at
-**    the specified physical address (kernel_addr, etc)
-** 4. prepare tags at tag_addr.  kernel_args[] is
-**    appended to the kernel commandline in the tags.
-** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
-** 6. if second_size != 0: jump to second_addr
-**    else: jump to kernel_addr
-*/
+ * It is expected that callers would explicitly specify which version of the
+ * boot image header they need to use.
+ */
+typedef struct boot_img_hdr_v0 boot_img_hdr;
+
+/* When a boot header is of version BOOT_HEADER_VERSION_ZERO, the structure of boot image is as
+ * follows:
+ *
+ * +-----------------+
+ * | boot header     | 1 page
+ * +-----------------+
+ * | kernel          | n pages
+ * +-----------------+
+ * | ramdisk         | m pages
+ * +-----------------+
+ * | second stage    | o pages
+ * +-----------------+
+ *
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. second is optional (second_size == 0 -> no second)
+ * 3. load each element (kernel, ramdisk, second) at
+ *    the specified physical address (kernel_addr, etc)
+ * 4. prepare tags at tag_addr.  kernel_args[] is
+ *    appended to the kernel commandline in the tags.
+ * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 6. if second_size != 0: jump to second_addr
+ *    else: jump to kernel_addr
+ */
+
+#define BOOT_HEADER_VERSION_ONE 1
+
+struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
+    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
+    uint64_t recovery_dtbo_offset; /* physical load addr */
+    uint32_t header_size;
+} __attribute__((packed));
+
+/* When the boot image header has a version of BOOT_HEADER_VERSION_ONE, the structure of the boot
+ * image is as follows:
+ *
+ * +-----------------+
+ * | boot header     | 1 page
+ * +-----------------+
+ * | kernel          | n pages
+ * +-----------------+
+ * | ramdisk         | m pages
+ * +-----------------+
+ * | second stage    | o pages
+ * +-----------------+
+ * | recovery dtbo   | p pages
+ * +-----------------+
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ * p = (recovery_dtbo_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
+ * 3. second is optional (second_size == 0 -> no second)
+ * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
+ *    the specified physical address (kernel_addr, etc)
+ * 5. prepare tags at tag_addr.  kernel_args[] is
+ *    appended to the kernel commandline in the tags.
+ * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 7. if second_size != 0: jump to second_addr
+ *    else: jump to kernel_addr
+ */
 
 #if 0
 typedef struct ptentry ptentry;
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index 5a13da2..ac20d05 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -57,7 +57,7 @@
         args.base + args.second_offset,                 # physical load addr
         args.base + args.tags_offset,                   # physical addr for kernel tags
         args.pagesize,                                  # flash page size we assume
-        0,                                              # future expansion: MUST be 0
+        args.header_version,                            # version of bootimage header
         (args.os_version << 11) | args.os_patch_level)) # os version and patch level
     args.output.write(pack('16s', args.board.encode())) # asciiz product name
     args.output.write(pack('512s', args.cmdline[:512].encode()))
@@ -66,10 +66,20 @@
     update_sha(sha, args.kernel)
     update_sha(sha, args.ramdisk)
     update_sha(sha, args.second)
+
+    if args.header_version > 0:
+        update_sha(sha, args.recovery_dtbo)
+
     img_id = pack('32s', sha.digest())
 
     args.output.write(img_id)
     args.output.write(pack('1024s', args.cmdline[512:].encode()))
+
+    if args.header_version > 0:
+        args.output.write(pack('I', filesize(args.recovery_dtbo)))           # size in bytes
+        args.output.write(pack('Q', args.base + args.recovery_dtbo_offset))  # physical load addr
+        args.output.write(pack('I', args.output.tell() + 4))                 # size of boot header
+
     pad_file(args.output, args.pagesize)
     return img_id
 
@@ -132,6 +142,7 @@
                         required=True)
     parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
     parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
+    parser.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
     parser.add_argument('--cmdline', help='extra arguments to be passed on the '
                         'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
     parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
@@ -139,6 +150,8 @@
     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
                         default=0x00f00000)
+    parser.add_argument('--recovery_dtbo_offset', help='recovery dtbo offset', type=parse_int,
+                        default=0x0f000000)
     parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
                         default=0)
     parser.add_argument('--os_patch_level', help='operating system patch level',
@@ -150,6 +163,7 @@
                         choices=[2**i for i in range(11,15)], default=2048)
     parser.add_argument('--id', help='print the image ID on standard output',
                         action='store_true')
+    parser.add_argument('--header_version', help='boot image header version', type=parse_int, default=0)
     parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
                         required=True)
     return parser.parse_args()
@@ -160,6 +174,8 @@
     write_padded_file(args.output, args.ramdisk, args.pagesize)
     write_padded_file(args.output, args.second, args.pagesize)
 
+    if args.header_version > 0:
+        write_padded_file(args.output, args.recovery_dtbo, args.pagesize)
 
 def main():
     args = parse_cmdline()
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg
new file mode 100755
index 0000000..8e42ec0
--- /dev/null
+++ b/mkbootimg/unpack_bootimg
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""unpacks the bootimage.
+
+Extracts the kernel, ramdisk, second bootloader and recovery dtbo images.
+"""
+
+from __future__ import print_function
+from argparse import ArgumentParser, FileType
+from struct import unpack
+import os
+
+
+def create_out_dir(dir_path):
+    """creates a directory 'dir_path' if it does not exist"""
+    if not os.path.exists(dir_path):
+        os.makedirs(dir_path)
+
+
+def extract_image(offset, size, bootimage, extracted_image_name):
+    """extracts an image from the bootimage"""
+    bootimage.seek(offset)
+    with open(extracted_image_name, 'wb') as file_out:
+        file_out.write(bootimage.read(size))
+
+
+def get_number_of_pages(image_size, page_size):
+    """calculates the number of pages required for the image"""
+    return (image_size + page_size - 1) / page_size
+
+
+def unpack_bootimage(args):
+    """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
+    boot_magic = unpack('8s', args.boot_img.read(8))
+    print('boot_magic: %s' % boot_magic)
+    kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
+    print('kernel_size: %s' % kernel_ramdisk_second_info[0])
+    print('kernel load address: %s' % kernel_ramdisk_second_info[1])
+    print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
+    print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
+    print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
+    print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
+    print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
+    print('page size: %s' % kernel_ramdisk_second_info[7])
+    print('boot image header version: %s' % kernel_ramdisk_second_info[8])
+    print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
+
+    product_name = unpack('16s', args.boot_img.read(16))
+    print('product name: %s' % product_name)
+    cmdline = unpack('512s', args.boot_img.read(512))
+    print('command line args: %s' % cmdline)
+
+    args.boot_img.read(32)  # ignore SHA
+
+    extra_cmdline = unpack('1024s', args.boot_img.read(1024))
+    print('additional command line args: %s' % extra_cmdline)
+
+    kernel_size = kernel_ramdisk_second_info[0]
+    ramdisk_size = kernel_ramdisk_second_info[2]
+    second_size = kernel_ramdisk_second_info[4]
+    page_size = kernel_ramdisk_second_info[7]
+    version = kernel_ramdisk_second_info[8]
+    if version > 0:
+        recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
+        print('recovery dtbo size: %s' % recovery_dtbo_size)
+        recovery_dtbo_address = unpack('Q', args.boot_img.read(8))[0]
+        print('recovery dtbo load address: %s' % recovery_dtbo_address)
+        boot_header_size = unpack('I', args.boot_img.read(4))[0]
+        print('boot header size: %s' % boot_header_size)
+    else:
+        recovery_dtbo_size = 0
+
+    # The first page contains the boot header
+    num_header_pages = 1
+
+    num_kernel_pages = get_number_of_pages(kernel_size, page_size)
+    kernel_offset = page_size * num_header_pages  # header occupies a page
+    image_info_list = [(kernel_offset, kernel_size, 'kernel')]
+
+    num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
+    ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
+                                 ) # header + kernel
+    image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
+
+    num_second_pages = get_number_of_pages(second_size, page_size)
+    second_offset = page_size * (
+        num_header_pages + num_kernel_pages + num_ramdisk_pages
+    )  # header + kernel + ramdisk
+    image_info_list.append((second_offset, second_size, 'second'))
+
+    if recovery_dtbo_size > 0:
+        dtbo_offset = page_size * (num_header_pages + num_kernel_pages +
+                                   num_ramdisk_pages + num_second_pages)
+        image_info_list.append((dtbo_offset, recovery_dtbo_size,
+                                'recovery_dtbo'))
+
+    for image_info in image_info_list:
+        extract_image(image_info[0], image_info[1], args.boot_img,
+                      os.path.join(args.out, image_info[2]))
+
+
+def parse_cmdline():
+    """parse command line arguments"""
+    parser = ArgumentParser(
+        description='Unpacks boot.img/recovery.img, extracts the kernel,'
+        'ramdisk, second bootloader and recovery dtbo')
+    parser.add_argument(
+        '--boot_img',
+        help='path to boot image',
+        type=FileType('rb'),
+        required=True)
+    parser.add_argument('--out', help='path to out binaries', default='out')
+    return parser.parse_args()
+
+
+def main():
+    """parse arguments and unpack boot image"""
+    args = parse_cmdline()
+    create_out_dir(args.out)
+    unpack_bootimage(args)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
index 6ee649a..7d66199 100644
--- a/property_service/property_info_checker/Android.bp
+++ b/property_service/property_info_checker/Android.bp
@@ -7,6 +7,7 @@
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
         "libbase",
+        "libsepol",
     ],
     srcs: ["property_info_checker.cpp"],
 }
diff --git a/property_service/property_info_checker/property_info_checker.cpp b/property_service/property_info_checker/property_info_checker.cpp
index e4f8264..52c4383 100644
--- a/property_service/property_info_checker/property_info_checker.cpp
+++ b/property_service/property_info_checker/property_info_checker.cpp
@@ -1,26 +1,150 @@
 #include <iostream>
+#include <memory>
 #include <string>
 #include <vector>
 
 #include <android-base/file.h>
-
+#include <property_info_parser/property_info_parser.h>
 #include <property_info_serializer/property_info_serializer.h>
+#include <sepol/context.h>
+#include <sepol/context_record.h>
+#include <sepol/handle.h>
+#include <sepol/policydb.h>
+#include <sepol/policydb/policydb.h>
 
 using android::base::ReadFileToString;
 using android::properties::BuildTrie;
 using android::properties::ParsePropertyInfoFile;
+using android::properties::PropertyInfoArea;
 using android::properties::PropertyInfoEntry;
 
+class ContextChecker {
+ public:
+  ContextChecker()
+      : policy_file_(nullptr),
+        sepol_handle_(nullptr),
+        sepol_policy_file_(nullptr),
+        sepol_policy_db_(nullptr) {}
+
+  ~ContextChecker() {
+    if (sepol_policy_db_ != nullptr) {
+      sepol_policydb_free(sepol_policy_db_);
+    }
+
+    if (sepol_policy_file_ != nullptr) {
+      sepol_policy_file_free(sepol_policy_file_);
+    }
+
+    if (sepol_handle_ != nullptr) {
+      sepol_handle_destroy(sepol_handle_);
+    }
+
+    if (policy_file_ != nullptr) {
+      fclose(policy_file_);
+    }
+  }
+
+  bool Initialize(const char* policy_file) {
+    policy_file_ = fopen(policy_file, "re");
+    if (policy_file_ == nullptr) {
+      std::cerr << "Could not open policy file, " << policy_file << std::endl;
+      return false;
+    }
+
+    sepol_handle_ = sepol_handle_create();
+    if (sepol_handle_ == nullptr) {
+      std::cerr << "Could not create policy handle." << std::endl;
+      return false;
+    }
+
+    if (sepol_policy_file_create(&sepol_policy_file_) < 0) {
+      std::cerr << "Could not create policy file." << std::endl;
+      return false;
+    }
+
+    if (sepol_policydb_create(&sepol_policy_db_) < 0) {
+      std::cerr << "Could not create policy db." << std::endl;
+      return false;
+    }
+
+    sepol_policy_file_set_fp(sepol_policy_file_, policy_file_);
+    sepol_policy_file_set_handle(sepol_policy_file_, sepol_handle_);
+
+    if (sepol_policydb_read(sepol_policy_db_, sepol_policy_file_) < 0) {
+      std::cerr << "Could not read policy file into policy db." << std::endl;
+      return false;
+    }
+
+    auto* attr =
+        reinterpret_cast<type_datum*>(hashtab_search(policy_db_->p_types.table, "property_type"));
+    if (attr == nullptr || attr->flavor != TYPE_ATTRIB) {
+      std::cerr << "'property_type' is not defined correctly." << std::endl;
+      return false;
+    }
+
+    property_type_bit_ = attr->s.value - 1;
+
+    return true;
+  }
+
+  bool CheckContext(const char* context) {
+    sepol_context_t* sepol_context_raw;
+    if (sepol_context_from_string(sepol_handle_, context, &sepol_context_raw) < 0) {
+      std::cerr << "Could not allocate context for " << context << std::endl;
+      return false;
+    }
+    auto sepol_context = std::unique_ptr<sepol_context_t, decltype(&sepol_context_free)>{
+        sepol_context_raw, sepol_context_free};
+
+    if (sepol_context_check(sepol_handle_, sepol_policy_db_, sepol_context.get()) < 0) {
+      std::cerr << "Sepol context check failed for " << context << std::endl;
+      return false;
+    }
+
+    const char* context_type = sepol_context_get_type(sepol_context.get());
+
+    auto* type =
+        reinterpret_cast<type_datum*>(hashtab_search(policy_db_->p_types.table, context_type));
+    if (type == nullptr) {
+      std::cerr << "Could not find context '" << context << "' in policy database" << std::endl;
+      return false;
+    }
+
+    if (type->flavor != TYPE_TYPE) {
+      std::cerr << "Context '" << context << "' is not defined as a type in policy database"
+                << std::endl;
+      return false;
+    }
+
+    if (!ebitmap_get_bit(&policy_db_->type_attr_map[type->s.value - 1], property_type_bit_)) {
+      std::cerr << "Context '" << context << "' does not have property_type attribute" << std::endl;
+      return false;
+    }
+
+    return true;
+  }
+
+ private:
+  FILE* policy_file_;
+  sepol_handle_t* sepol_handle_;
+  sepol_policy_file_t* sepol_policy_file_;
+  union {
+    sepol_policydb_t* sepol_policy_db_;
+    policydb_t* policy_db_;
+  };
+  unsigned int property_type_bit_;
+};
+
 int main(int argc, char** argv) {
-  if (argc < 2) {
-    std::cerr << "A list of property info files to be checked is expected on the command line"
-              << std::endl;
+  if (argc < 3) {
+    std::cerr << "usage: " << argv[0]
+              << " COMPILED_SEPOLICY PROPERTY_INFO_FILE [PROPERTY_INFO_FILE]..." << std::endl;
     return -1;
   }
 
   auto property_info_entries = std::vector<PropertyInfoEntry>{};
 
-  for (int i = 1; i < argc; ++i) {
+  for (int i = 2; i < argc; ++i) {
     auto filename = argv[i];
     auto file_contents = std::string{};
     if (!ReadFileToString(filename, &file_contents)) {
@@ -47,5 +171,17 @@
     return -1;
   }
 
+  auto checker = ContextChecker{};
+  if (!checker.Initialize(argv[1])) {
+    return -1;
+  }
+
+  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(serialized_contexts.data());
+  for (size_t i = 0; i < property_info_area->num_contexts(); ++i) {
+    if (!checker.CheckContext(property_info_area->context(i))) {
+      return -1;
+    }
+  }
+
   return 0;
 }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index feb100e..f488ed5 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -93,6 +93,23 @@
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
 endif
+
+# For /odm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /odm partition. Those symlinks are for devices
+# without /odm partition. For devices with /odm partition, mount odm.img under
+# /odm will hide those symlinks.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/app $(TARGET_ROOT_OUT)/odm/app
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/bin $(TARGET_ROOT_OUT)/odm/bin
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/etc $(TARGET_ROOT_OUT)/odm/etc
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/firmware $(TARGET_ROOT_OUT)/odm/firmware
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/framework $(TARGET_ROOT_OUT)/odm/framework
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib $(TARGET_ROOT_OUT)/odm/lib
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib64 $(TARGET_ROOT_OUT)/odm/lib64
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/overlay
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
+
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index d55ec57..ca6aafe 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -10,6 +10,9 @@
 dir.legacy = /odm
 dir.legacy = /sbin
 
+# Except for /postinstall, where only /system is searched
+dir.postinstall = /postinstall
+
 [legacy]
 namespace.default.isolated = false
 
@@ -23,3 +26,15 @@
 namespace.default.asan.search.paths +=           /odm/${LIB}
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
+
+###############################################################################
+# Namespace config for binaries under /postinstall.
+# Only one default namespace is defined and it has no directories other than
+# /system/lib in the search paths. This is because linker calls realpath on the
+# search paths and this causes selinux denial if the paths (/vendor, /odm) are
+# not allowed to the poinstall binaries. There is no reason to allow the
+# binaries to access the paths.
+###############################################################################
+[postinstall]
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index c8d87c8..94465f4 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -24,6 +24,8 @@
 dir.system = /data/benchmarktest
 dir.system = /data/benchmarktest64
 
+dir.postinstall = /postinstall
+
 [system]
 additional.namespaces = sphal,vndk,rs
 
@@ -54,6 +56,9 @@
 namespace.default.permitted.paths += /vendor/framework
 namespace.default.permitted.paths += /vendor/app
 namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /odm/framework
+namespace.default.permitted.paths += /odm/app
+namespace.default.permitted.paths += /odm/priv-app
 namespace.default.permitted.paths += /oem/app
 namespace.default.permitted.paths += /product/framework
 namespace.default.permitted.paths += /product/app
@@ -74,6 +79,9 @@
 namespace.default.asan.permitted.paths += /vendor/framework
 namespace.default.asan.permitted.paths += /vendor/app
 namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /odm/framework
+namespace.default.asan.permitted.paths += /odm/app
+namespace.default.asan.permitted.paths += /odm/priv-app
 namespace.default.asan.permitted.paths += /oem/app
 namespace.default.asan.permitted.paths += /product/framework
 namespace.default.asan.permitted.paths += /product/app
@@ -309,3 +317,15 @@
 
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
+
+###############################################################################
+# Namespace config for binaries under /postinstall.
+# Only one default namespace is defined and it has no directories other than
+# /system/lib in the search paths. This is because linker calls realpath on the
+# search paths and this causes selinux denial if the paths (/vendor, /odm) are
+# not allowed to the poinstall binaries. There is no reason to allow the
+# binaries to access the paths.
+###############################################################################
+[postinstall]
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 5256cb1..1fd4195 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -24,6 +24,8 @@
 dir.system = /data/benchmarktest
 dir.system = /data/benchmarktest64
 
+dir.postinstall = /postinstall
+
 [system]
 additional.namespaces = sphal,vndk,rs
 
@@ -222,3 +224,15 @@
 namespace.default.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.asan.search.paths += /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
+
+###############################################################################
+# Namespace config for binaries under /postinstall.
+# Only one default namespace is defined and it has no directories other than
+# /system/lib in the search paths. This is because linker calls realpath on the
+# search paths and this causes selinux denial if the paths (/vendor, /odm) are
+# not allowed to the poinstall binaries. There is no reason to allow the
+# binaries to access the paths.
+###############################################################################
+[postinstall]
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a213ffb..1462570 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -573,6 +573,9 @@
     hostname localhost
     domainname localdomain
 
+    # IPsec SA default expiration length
+    write /proc/sys/net/core/xfrm_acq_expires 3600
+
     # Memory management.  Basic kernel parameters, and allow the high
     # level system server to be able to adjust the kernel OOM driver
     # parameters to match how it is managing things.
diff --git a/storaged/OWNERS b/storaged/OWNERS
index 7445270..c6feee8 100644
--- a/storaged/OWNERS
+++ b/storaged/OWNERS
@@ -1 +1,2 @@
 jinqian@google.com
+salyzyn@google.com